diff --git a/setup.cfg b/setup.cfg index 05cae80..f57cd01 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,12 @@ scripts = synergy.managers = timer = synergy.examples.timer_manager:TimerManager +synergy.commands = + list = synergy.client.command:LIST + status = synergy.client.command:STATUS + start = synergy.client.command:START + stop = synergy.client.command:STOP + [build_sphinx] source-dir = doc/source build-dir = doc/build diff --git a/synergy/client/__init__.py b/synergy/client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/synergy/client/command.py b/synergy/client/command.py new file mode 100644 index 0000000..5ed4370 --- /dev/null +++ b/synergy/client/command.py @@ -0,0 +1,258 @@ +import requests + +__author__ = "Lisa Zangrando" +__email__ = "lisa.zangrando[AT]pd.infn.it" +__copyright__ = """Copyright (c) 2015 INFN - INDIGO-DataCloud +All Rights Reserved + +Licensed under the Apache License, Version 2.0; +you may not use this file except in compliance with the +License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. +See the License for the specific language governing +permissions and limitations under the License.""" + +""" +class BaseCommand(object): + + def __init__(self, name): + self.name = name + self.parameters = {} + self.results = {} + + def getName(self): + return self.name + + def getParameters(self): + return self.parameters + + def addParameter(self, name, value): + self.parameters[name] = value + + def getParameter(self, name): + return self.parameters.get(name, None) + + def getResults(self): + return self.results + + def addResult(self, name, value): + self.results[name] = value + + def getResult(self, name): + return self.getResults().get(name, None) + + def setResults(self, data): + self.results = data + + +class HTTPCommand(BaseCommand): +""" + + +class HTTPCommand(object): + + def __init__(self, name): + self.name = name + + def getName(self): + return self.name + + # def __init__(self, name): + # super(HTTPCommand, self).__init__(name) + + def configureParser(self, subparser): + raise NotImplementedError("not implemented!") + + def log(self): + raise NotImplementedError("not implemented!") + + def sendRequest(self, synergy_url, payload=None): + request = requests.get(synergy_url, params=payload) + + if request.status_code != requests.codes.ok: + # print(request.reason) + # print(request.status_code) + request.raise_for_status() + + self.results = request.json() + # self.setResults(request.json()) + return request + + def getResults(self): + return self.results + + +class LIST(HTTPCommand): + + def __init__(self): + super(LIST, self).__init__("list") + + def configureParser(self, subparser): + subparser.add_parser("list", add_help=True, help="list the managers") + + def sendRequest(self, synergy_url, args=None): + super(LIST, self).sendRequest(synergy_url + "/synergy/list") + + def log(self): + results = self.getResults() + + max_project_id = max(len(max(results, key=len)), len("manager")) + separator_str = "-" * (max_project_id + 4) + "\n" + format_str = "| {0:%ss} |\n" % (max_project_id) + + msg = separator_str + msg += format_str.format("manager") + msg += separator_str + + for manager in results: + msg += format_str.format(manager) + + msg += separator_str + print(msg) + + +class START(HTTPCommand): + + def __init__(self): + super(START, self).__init__("start") + + def configureParser(self, subparser): + parser = subparser.add_parser("start", + add_help=True, + help="start the managers") + + parser.add_argument("manager", help="the manager to be started") + + def sendRequest(self, synergy_url, args): + super(START, self).sendRequest(synergy_url + "/synergy/start", + {"manager": args.manager}) + + def log(self): + results = self.getResults() + + max_manager = max(len(max(results.keys(), key=len)), len("manager")) + + max_status = len("status") + max_msg = len("message") + + for result in results.values(): + max_status = max(len(str(result["status"])), max_status) + max_msg = max(len(str(result["message"])), max_msg) + + separator_str = "-" * (max_manager + max_status + max_msg + 10) + "\n" + + format_str = "| {0:%ss} | {1:%ss} | {2:%ss} |\n" % (max_manager, + max_status, + max_msg) + + msg = separator_str + msg += format_str.format("manager", "status", "message") + msg += separator_str + + for manager, values in results.items(): + msg += format_str.format(manager, + values["status"], + values["message"]) + + msg += separator_str + print(msg) + + +class STOP(HTTPCommand): + + def __init__(self): + super(STOP, self).__init__("stop") + + def configureParser(self, subparser): + parser = subparser.add_parser("stop", + add_help=True, + help="stop the managers") + + parser.add_argument("manager", help="the manager to be stopped") + + def sendRequest(self, synergy_url, args): + super(STOP, self).sendRequest(synergy_url + "/synergy/stop", + {"manager": args.manager}) + + def log(self): + results = self.getResults() + + max_manager = max(len(max(results.keys(), key=len)), len("manager")) + max_status = len("status") + max_msg = len("message") + + for result in results.values(): + max_status = max(len(str(result["status"])), max_status) + max_msg = max(len(str(result["message"])), max_msg) + + separator_str = "-" * (max_manager + max_status + max_msg + 10) + "\n" + format_str = "| {0:%ss} | {1:%ss} | {2:%ss} |\n" % (max_manager, + max_status, + max_msg) + + msg = separator_str + msg += format_str.format("manager", "status", "message") + msg += separator_str + + for manager, values in results.items(): + msg += format_str.format(manager, + values["status"], + values["message"]) + + msg += separator_str + print(msg) + + +class STATUS(HTTPCommand): + + def __init__(self): + super(STATUS, self).__init__("status") + + def configureParser(self, subparser): + parser = subparser.add_parser("status", + add_help=True, + help="retrieve the manager's status") + + parser.add_argument("manager", nargs='*', help="the managers list") + + def sendRequest(self, synergy_url, args): + super(STATUS, self).sendRequest(synergy_url + "/synergy/status", + {"manager": args.manager}) + + def log(self): + results = self.getResults() + + max_project_id = max(len(max(results.keys(), key=len)), len("manager")) + max_value = max(len(max(results.values(), key=len)), len("status")) + separator_str = "-" * (max_project_id + max_value + 7) + "\n" + format_str = "| {0:%ss} | {1:%ss} |\n" % (max_project_id, max_value) + + msg = separator_str + msg += format_str.format("manager", "status") + msg += separator_str + + for manager, status in results.items(): + msg += format_str.format(manager, status) + + msg += separator_str + print(msg) + + +class EXECUTE(HTTPCommand): + + def __init__(self, name): + super(EXECUTE, self).__init__(name) + + def sendRequest(self, synergy_url, manager, command, args=None): + payload = {"manager": manager, + "command": command, + "args": args} + + super(EXECUTE, self).sendRequest(synergy_url + "/synergy/execute", + payload) diff --git a/synergy/client/keystone_v3.py b/synergy/client/keystone_v3.py new file mode 100644 index 0000000..6629023 --- /dev/null +++ b/synergy/client/keystone_v3.py @@ -0,0 +1,589 @@ +import json +import os.path +import requests + +from datetime import datetime + + +__author__ = "Lisa Zangrando" +__email__ = "lisa.zangrando[AT]pd.infn.it" +__copyright__ = """Copyright (c) 2015 INFN - INDIGO-DataCloud +All Rights Reserved + +Licensed under the Apache License, Version 2.0; +you may not use this file except in compliance with the +License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. +See the License for the specific language governing +permissions and limitations under the License.""" + + +class Trust(object): + + def __init__(self, data): + data = data["trust"] + + self.id = data["id"] + self.impersonations = data["impersonation"] + self.roles_links = data["roles_links"] + self.trustor_user_id = data["trustor_user_id"] + self.trustee_user_id = data["trustee_user_id"] + self.links = data["links"] + self.roles = data["roles"] + self.remaining_uses = data["remaining_uses"] + self.expires_at = None + + if data["expires_at"] is not None: + self.expires_at = datetime.strptime(data["expires_at"], + "%Y-%m-%dT%H:%M:%S.%fZ") + self.project_id = data["project_id"] + + def getId(self): + return self.id + + def isImpersonations(self): + return self.impersonations + + def getRolesLinks(self): + return self.roles_links + + def getTrustorUserId(self): + return self.trustor_user_id + + def getTrusteeUserId(self): + return self.trustee_user_id + + def getlinks(self): + return self.links + + def getProjectId(self): + return self.project_id + + def getRoles(self): + return self.roles + + def getRemainingUses(self): + return self.remaining_uses + + def getExpiration(self): + return self.expires_at + + def isExpired(self): + if self.getExpiration() is None: + return False + + return self.getExpiration() < datetime.utcnow() + + +class Token(object): + + def __init__(self, token, data): + self.id = token + + data = data["token"] + self.roles = data["roles"] + self.catalog = data["catalog"] + self.issued_at = datetime.strptime(data["issued_at"], + "%Y-%m-%dT%H:%M:%S.%fZ") + self.expires_at = datetime.strptime(data["expires_at"], + "%Y-%m-%dT%H:%M:%S.%fZ") + self.project = data["project"] + self.user = data["user"] + self.extras = data["extras"] + + def getCatalog(self, service_name=None, interface="public"): + if service_name: + for service in self.catalog: + if service["name"] == service_name: + for endpoint in service["endpoints"]: + if endpoint["interface"] == interface: + return endpoint + return None + else: + return self.catalog + + def getExpiration(self): + return self.expires_at + + def getId(self): + return self.id + + def getExtras(self): + return self.extras + + def getProject(self): + return self.project + + def getRoles(self): + return self.roles + + def getUser(self): + return self.user + + def isAdmin(self): + if not self.roles: + return False + + for role in self.roles: + if role["name"] == "admin": + return True + + return False + + def issuedAt(self): + return self.issued_at + + def isExpired(self): + return self.getExpiration() < datetime.utcnow() + + def save(self, filename): + # save to file + with open(filename, 'w') as f: + token = {} + token["catalog"] = self.catalog + token["extras"] = self.extras + token["user"] = self.user + token["project"] = self.project + token["roles"] = self.roles + token["roles"] = self.roles + token["issued_at"] = self.issued_at.isoformat() + token["expires_at"] = self.expires_at.isoformat() + + data = {"id": self.id, "token": token} + + json.dump(data, f) + + @classmethod + def load(cls, filename): + if not os.path.isfile(".auth_token"): + return None + + # load from file: + with open(filename, 'r') as f: + try: + data = json.load(f) + return Token(data["id"], data) + # if the file is empty the ValueError will be thrown + except ValueError as ex: + raise ex + + def isotime(self, at=None, subsecond=False): + """Stringify time in ISO 8601 format.""" + if not at: + at = datetime.utcnow() + + if not subsecond: + st = at.strftime('%Y-%m-%dT%H:%M:%S') + else: + st = at.strftime('%Y-%m-%dT%H:%M:%S.%f') + + if at.tzinfo: + tz = at.tzinfo.tzname(None) + else: + tz = 'UTC' + + st += ('Z' if tz == 'UTC' else tz) + return st + + """The trustor or grantor of a trust is the person who creates the trust. + The trustor is the one who contributes property to the trust. + The trustee is the person who manages the trust, and is usually appointed + by the trustor. The trustor is also often the trustee in living trusts. + """ + def trust(self, trustee_user, expires_at=None, + project_id=None, roles=None, impersonation=True): + if self.isExpired(): + raise Exception("token expired!") + + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "python-novaclient", + "X-Auth-Token": self.getId()} + + if roles is None: + roles = self.getRoles() + + if project_id is None: + project_id = self.getProject().get("id") + + data = {} + data["trust"] = {"impersonation": impersonation, + "project_id": project_id, + "roles": roles, + "trustee_user_id": trustee_user, + "trustor_user_id": self.getUser().get("id")} + + if expires_at is not None: + data["trust"]["expires_at"] = self.isotime(expires_at, True) + + endpoint = self.getCatalog(service_name="keystone") + + if not endpoint: + raise Exception("keystone endpoint not found!") + + if "v2.0" in endpoint["url"]: + endpoint["url"] = endpoint["url"].replace("v2.0", "v3") + + response = requests.post(url=endpoint["url"] + "/OS-TRUST/trusts", + headers=headers, + data=json.dumps(data)) + + if response.status_code != requests.codes.ok: + response.raise_for_status() + + if not response.text: + raise Exception("trust token failed!") + + return Trust(response.json()) + + +class KeystoneClient(object): + + def __init__(self, auth_url, username, password, project_id=None, + project_name=None, timeout=None, + default_trust_expiration=None): + self.auth_url = auth_url + self.username = username + self.password = password + self.project_id = project_id + self.project_name = project_name + self.timeout = timeout + self.token = None + + if default_trust_expiration: + self.default_trust_expiration = default_trust_expiration + else: + self.default_trust_expiration = 24 + + def authenticate(self): + if self.token is not None: + if self.token.isExpired(): + try: + self.deleteToken(self.token.getId()) + except requests.exceptions.HTTPError: + pass + else: + return + + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "python-novaclient"} + + identity = {"methods": ["password"], + "password": {"user": {"name": self.username, + "domain": {"id": "default"}, + "password": self.password}}} + + data = {"auth": {}} + data["auth"]["identity"] = identity + + if self.project_name: + data["auth"]["scope"] = {"project": {"name": self.project_name, + "domain": {"id": "default"}}} + + if self.project_id: + data["auth"]["scope"] = {"project": {"id": self.project_id, + "domain": {"id": "default"}}} + + response = requests.post(url=self.auth_url + "/auth/tokens", + headers=headers, + data=json.dumps(data), + timeout=self.timeout) + + if response.status_code != requests.codes.ok: + response.raise_for_status() + + if not response.text: + raise Exception("authentication failed!") + + # print(response.__dict__) + + token_subject = response.headers["X-Subject-Token"] + token_data = response.json() + + self.token = Token(token_subject, token_data) + + def getUser(self, id): + try: + response = self.getResource("users/%s" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the user info (id=%r): %s" + % (id, response["error"]["message"])) + + if response: + response = response["user"] + + return response + + def getUsers(self): + try: + response = self.getResource("users", "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the users list: %s" + % response["error"]["message"]) + + if response: + response = response["users"] + + return response + + def getUserProjects(self, id): + try: + response = self.getResource("users/%s/projects" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the users's projects " + "(id=%r): %s" % (id, response["error"]["message"])) + + if response: + response = response["projects"] + + return response + + def getUserRoles(self, user_id, project_id): + try: + response = self.getResource("/projects/%s/users/%s/roles" + % (project_id, user_id), "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the user's roles (usrId=%r, " + "prjId=%r): %s" % (user_id, + project_id, + response["error"]["message"])) + + if response: + response = response["roles"] + + return response + + def getProject(self, id): + try: + response = self.getResource("/projects/%s" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the project (id=%r, " + % (id, response["error"]["message"])) + + if response: + response = response["project"] + + return response + + def getProjects(self): + try: + response = self.getResource("/projects", "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the projects list: %s" + % response["error"]["message"]) + + if response: + response = response["projects"] + + return response + + def getRole(self, id): + try: + response = self.getResource("/roles/%s" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the role info (id=%r): %s" + % (id, response["error"]["message"])) + + if response: + response = response["role"] + + return response + + def getRoles(self): + try: + response = self.getResource("/roles", "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the roles list: %s" + % response["error"]["message"]) + + if response: + response = response["roles"] + + return response + + def getToken(self): + self.authenticate() + return self.token + + def deleteToken(self, id): + if self.token is None: + return + + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "python-novaclient", + "X-Auth-Project-Id": self.token.getProject()["name"], + "X-Auth-Token": self.token.getId(), + "X-Subject-Token": id} + + response = requests.delete(url=self.auth_url + "/auth/tokens", + headers=headers, + timeout=self.timeout) + + self.token = None + + if response.status_code != requests.codes.ok: + response.raise_for_status() + + def validateToken(self, id): + self.authenticate() + + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "python-novaclient", + "X-Auth-Project-Id": self.token.getProject()["name"], + "X-Auth-Token": self.token.getId(), + "X-Subject-Token": id} + + response = requests.get(url=self.auth_url + "/auth/tokens", + headers=headers, + timeout=self.timeout) + + if response.status_code != requests.codes.ok: + response.raise_for_status() + + if not response.text: + raise Exception("token not found!") + + token_subject = response.headers["X-Subject-Token"] + token_data = response.json() + + return Token(token_subject, token_data) + + def getEndpoint(self, id=None, service_id=None): + if id: + try: + response = self.getResource("/endpoints/%s" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the endpoint (id=%r): %s" + % (id, response["error"]["message"])) + if response: + response = response["endpoint"] + + return response + elif service_id: + try: + endpoints = self.getEndpoints() + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the endpoints list" + "(serviceId=%r): %s" + % response["error"]["message"]) + + if endpoints: + for endpoint in endpoints: + if endpoint["service_id"] == service_id: + return endpoint + + return None + + def getEndpoints(self): + try: + response = self.getResource("/endpoints", "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the endpoints list: %s" + % response["error"]["message"]) + + if response: + response = response["endpoints"] + + return response + + def getService(self, id=None, name=None): + if id: + try: + response = self.getResource("/services/%s" % id, "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the service info (id=%r)" + ": %s" % (id, response["error"]["message"])) + + if response: + response = response["service"] + return response + elif name: + services = self.getServices() + + if services: + for service in services: + if service["name"] == name: + return service + + return None + + def getServices(self): + try: + response = self.getResource("/services", "GET") + except requests.exceptions.HTTPError as ex: + response = ex.response.json() + raise Exception("error on retrieving the services list: %s" + % response["error"]["message"]) + + if response: + response = response["services"] + + return response + + def getResource(self, resource, method, data=None): + self.authenticate() + + url = self.auth_url + "/" + resource + + headers = {"Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "python-novaclient", + "X-Auth-Project-Id": self.token.getProject()["name"], + "X-Auth-Token": self.token.getId()} + + if method == "GET": + response = requests.get(url, + headers=headers, + params=data, + timeout=self.timeout) + elif method == "POST": + response = requests.post(url, + headers=headers, + data=json.dumps(data), + timeout=self.timeout) + elif method == "PUT": + response = requests.put(url, + headers=headers, + data=json.dumps(data), + timeout=self.timeout) + elif method == "HEAD": + response = requests.head(url, + headers=headers, + data=json.dumps(data), + timeout=self.timeout) + elif method == "DELETE": + response = requests.delete(url, + headers=headers, + data=json.dumps(data), + timeout=self.timeout) + else: + raise Exception("wrong HTTP method: %s" % method) + + if response.status_code != requests.codes.ok: + response.raise_for_status() + + if response.text: + return response.json() + else: + return None diff --git a/synergy/client/shell.py b/synergy/client/shell.py new file mode 100644 index 0000000..f498dd6 --- /dev/null +++ b/synergy/client/shell.py @@ -0,0 +1,204 @@ +import os +import os.path +import requests +import sys + +from argparse import ArgumentParser +from pkg_resources import iter_entry_points +from synergy.client import keystone_v3 + +__author__ = "Lisa Zangrando" +__email__ = "lisa.zangrando[AT]pd.infn.it" +__copyright__ = """Copyright (c) 2015 INFN - INDIGO-DataCloud +All Rights Reserved + +Licensed under the Apache License, Version 2.0; +you may not use this file except in compliance with the +License. You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. +See the License for the specific language governing +permissions and limitations under the License.""" + +COMMANDS_ENTRY_POINT = "synergy.commands" # used to discover Synergy commands + + +def main(): + try: + parser = ArgumentParser(prog="synergy", + epilog="Command-line interface to the" + " OpenStack Synergy API.") + + # Global arguments + parser.add_argument("--version", action="version", version="v1.0") + + parser.add_argument("--debug", + default=False, + action="store_true", + help="print debugging output") + + parser.add_argument("--os-username", + metavar="", + default=os.environ.get("OS_USERNAME"), + help="defaults to env[OS_USERNAME]") + + parser.add_argument("--os-password", + metavar="", + default=os.environ.get("OS_PASSWORD"), + help="defaults to env[OS_PASSWORD]") + + parser.add_argument("--os-project-name", + metavar="", + default=os.environ.get("OS_PROJECT_NAME"), + help="defaults to env[OS_PROJECT_NAME]") + + parser.add_argument("--os-project-id", + metavar="", + default=os.environ.get("OS_PROJECT_ID"), + help="defaults to env[OS_PROJECT_ID]") + + parser.add_argument("--os-auth-token", + metavar="", + default=os.environ.get("OS_AUTH_TOKEN", None), + help="defaults to env[OS_AUTH_TOKEN]") + + parser.add_argument('--os-auth-token-cache', + default=os.environ.get("OS_AUTH_TOKEN_CACHE", + False), + action='store_true', + help="Use the auth token cache. Defaults to False " + "if env[OS_AUTH_TOKEN_CACHE] is not set") + + parser.add_argument("--os-auth-url", + metavar="", + default=os.environ.get("OS_AUTH_URL"), + help="defaults to env[OS_AUTH_URL]") + + parser.add_argument("--os-auth-system", + metavar="", + default=os.environ.get("OS_AUTH_SYSTEM"), + help="defaults to env[OS_AUTH_SYSTEM]") + + parser.add_argument("--bypass-url", + metavar="", + dest="bypass_url", + help="use this API endpoint instead of the " + "Service Catalog") + + parser.add_argument("--os-cacert", + metavar="", + default=os.environ.get("OS_CACERT", None), + help="Specify a CA bundle file to use in verifying" + " a TLS (https) server certificate. Defaults " + "to env[OS_CACERT]") + """ + parser.add_argument("--insecure", + default=os.environ.get("INSECURE", False), + action="store_true", + help="explicitly allow Synergy's client to perform" + " \"insecure\" SSL (https) requests. The " + "server's certificate will not be verified " + "against any certificate authorities. This " + "option should be used with caution.") + """ + + subparser = parser.add_subparsers(help="commands", dest="command_name") + commands = {} + + for entry in iter_entry_points(COMMANDS_ENTRY_POINT): + command_class = entry.load() + command = command_class() + # command = command_class(*args, **kwargs) + command.configureParser(subparser) + commands[entry.name] = command + + args = parser.parse_args(sys.argv[1:]) + + # print("args %s" % args) + + os_username = args.os_username + os_password = args.os_password + os_project_name = args.os_project_name + # os_project_id = args.os_project_id + os_auth_token = args.os_auth_token + os_auth_token_cache = args.os_auth_token_cache + os_auth_url = args.os_auth_url + # os_auth_system = args.os_auth_system + # insecure = args.insecure + bypass_url = args.bypass_url + # cacert = args.os_cacert + command_name = args.command_name + + if not os_username: + raise Exception("'os-username' not defined!") + + if not os_password: + raise Exception("'os-password' not defined!") + + if not os_project_name: + raise Exception("'os-project-name' not defined!") + + if not os_auth_url: + raise Exception("'os-auth-url' not defined!") + + client = keystone_v3.KeystoneClient(auth_url=os_auth_url, + username=os_username, + password=os_password, + project_name=os_project_name) + """ + client.authenticate() + + token = client.getToken() + + print("os_auth_token=%s" % os_auth_token) + print("os_auth_token_cache=%s" % os_auth_token_cache) + """ + token = None + + if os_auth_token: + token = os_auth_token + elif os_auth_token_cache: + token = keystone_v3.Token.load(".auth_token") + # print("token is expired? %s" % token.isExpired()) + if token is None or token.isExpired(): + client.authenticate() + token = client.getToken() + token.save(".auth_token") + else: + client.authenticate() + token = client.getToken() + + synergy_url = None + if bypass_url: + synergy_url = bypass_url + else: + synergy_service = client.getService(name="synergy") + + synergy_endpoint = client.getEndpoint( + service_id=synergy_service["id"]) + + synergy_url = synergy_endpoint["url"] + + if command_name not in commands: + print("command %r not found!" % command_name) + + commands[command_name].sendRequest(synergy_url, args) + commands[command_name].log() + except KeyboardInterrupt as e: + print("Shutting down synergyclient") + sys.exit(1) + except requests.exceptions.HTTPError as e: + print("HTTPError: %s" % e.response._content) + sys.exit(1) + except Exception as e: + print("ERROR: %s" % e) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/synergy/service.py b/synergy/service.py index bf619fd..645883f 100644 --- a/synergy/service.py +++ b/synergy/service.py @@ -240,6 +240,56 @@ class Synergy(service.Service): manager_name = None command = None + query = environ.get("QUERY_STRING", None) + + if query: + parameters = parse_qs(query) + LOG.info(parameters) + if "manager" in parameters: + manager_name = escape(parameters['manager'][0]) + + if "command" in parameters: + command = escape(parameters['command'][0]) + + if "args" in parameters: + manager_args = escape(parameters['args'][0]) + manager_args = manager_args.replace("'", "\"") + manager_args = json.loads(manager_args) + else: + manager_args = {} + + if not query or not manager_name or not command: + start_response("404 NOT FOUND", [("Content-Type", "text/plain")]) + return ["wrong command"] + + if manager_name in self.managers: + manager = self.managers[manager_name] + try: + result = manager.execute(command=command, **manager_args) + + if not isinstance(result, dict): + try: + result = result.toDict() + except Exception: + result = result.__dict__ + + LOG.info("command result %s" % result) + + start_response("200 OK", [("Content-Type", "text/html")]) + return ["%s" % json.dumps(result)] + except Exception as ex: + LOG.info("executeCommand error: %s" % ex) + start_response("404 NOT FOUND", + [("Content-Type", "text/plain")]) + return ["error: %s" % ex] + else: + start_response("404 NOT FOUND", [("Content-Type", "text/plain")]) + return ["manager %r not found!" % manager_name] + + def executeCommand2(self, environ, start_response): + manager_name = None + command = None + synergySerializer = serializer.SynergySerializer() query = environ.get("QUERY_STRING", None) # LOG.info("QUERY_STRING %s" % query)