add synergy CLI
Change-Id: Ibd60ee3f448873e381126ff35a635e84971ab137
This commit is contained in:
parent
31d50774e7
commit
a56f18470f
@ -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
|
||||
|
0
synergy/client/__init__.py
Normal file
0
synergy/client/__init__.py
Normal file
258
synergy/client/command.py
Normal file
258
synergy/client/command.py
Normal file
@ -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)
|
589
synergy/client/keystone_v3.py
Normal file
589
synergy/client/keystone_v3.py
Normal file
@ -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
|
204
synergy/client/shell.py
Normal file
204
synergy/client/shell.py
Normal file
@ -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="<auth-user-name>",
|
||||
default=os.environ.get("OS_USERNAME"),
|
||||
help="defaults to env[OS_USERNAME]")
|
||||
|
||||
parser.add_argument("--os-password",
|
||||
metavar="<auth-password>",
|
||||
default=os.environ.get("OS_PASSWORD"),
|
||||
help="defaults to env[OS_PASSWORD]")
|
||||
|
||||
parser.add_argument("--os-project-name",
|
||||
metavar="<auth-project-name>",
|
||||
default=os.environ.get("OS_PROJECT_NAME"),
|
||||
help="defaults to env[OS_PROJECT_NAME]")
|
||||
|
||||
parser.add_argument("--os-project-id",
|
||||
metavar="<auth-project-id>",
|
||||
default=os.environ.get("OS_PROJECT_ID"),
|
||||
help="defaults to env[OS_PROJECT_ID]")
|
||||
|
||||
parser.add_argument("--os-auth-token",
|
||||
metavar="<auth-token>",
|
||||
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="<auth-url>",
|
||||
default=os.environ.get("OS_AUTH_URL"),
|
||||
help="defaults to env[OS_AUTH_URL]")
|
||||
|
||||
parser.add_argument("--os-auth-system",
|
||||
metavar="<auth-system>",
|
||||
default=os.environ.get("OS_AUTH_SYSTEM"),
|
||||
help="defaults to env[OS_AUTH_SYSTEM]")
|
||||
|
||||
parser.add_argument("--bypass-url",
|
||||
metavar="<bypass-url>",
|
||||
dest="bypass_url",
|
||||
help="use this API endpoint instead of the "
|
||||
"Service Catalog")
|
||||
|
||||
parser.add_argument("--os-cacert",
|
||||
metavar="<ca-certificate>",
|
||||
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()
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user