Compare commits

...

2 Commits
master ... v2.1

Author SHA1 Message Date
Adolfo Gómez García
16f05920b5 Fix 2018-04-04 14:13:05 +02:00
Adolfo Gómez García
98befc7cee Removed cache for credentials on OpenStack platform 2018-02-27 06:49:24 +01:00

View File

@@ -38,18 +38,16 @@ from uds.core.util.Cache import Cache
import logging import logging
import requests import requests
import json import json
import dateutil.parser # import dateutil.parser
import hashlib import hashlib
import six import six
__updated__ = '2018-03-01'
__updated__ = '2017-03-21'
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Required: Authentication v3 # Required: Authentication v3
# This is a vary basic implementation for what we need from openstack # This is a vary basic implementation for what we need from openstack
# This does not includes (nor it is intention) full API implementation, just the parts we need # This does not includes (nor it is intention) full API implementation, just the parts we need
# Theese are related to auth, compute & network basically # Theese are related to auth, compute & network basically
@@ -60,9 +58,17 @@ logger = logging.getLogger(__name__)
# Do not verify SSL conections right now # Do not verify SSL conections right now
VERIFY_SSL = False VERIFY_SSL = False
class AuthenticationRequiredException(Exception):
pass
# Helpers # Helpers
def ensureResponseIsValid(response, errMsg=None): def ensureResponseIsValid(response, errMsg=None):
if response.ok is False: if response.ok is False:
if response.status_code == 401:
raise AuthenticationRequiredException('Authentication required')
try: try:
_x, err = response.json().popitem() # Extract any key, in case of error is expected to have only one top key so this will work _x, err = response.json().popitem() # Extract any key, in case of error is expected to have only one top key so this will work
errMsg = errMsg + ': {message}'.format(**err) errMsg = errMsg + ': {message}'.format(**err)
@@ -70,7 +76,7 @@ def ensureResponseIsValid(response, errMsg=None):
pass # If error geting error message, simply ignore it (will be loged on service log anyway) pass # If error geting error message, simply ignore it (will be loged on service log anyway)
if errMsg is None: if errMsg is None:
errMsg = 'Error checking response' errMsg = 'Error checking response'
logger.error('{}: {}'.format(errMsg, response.content)) logger.error('{}: {} ({})'.format(errMsg, response.content, response.status_code))
raise Exception(errMsg) raise Exception(errMsg)
@@ -96,22 +102,42 @@ def getRecurringUrlJson(url, headers, key, params=None, errMsg=None, timeout=10)
# Decorators # Decorators
def authRequired(func): def authRequired(func):
def ensurer(obj, *args, **kwargs): def ensurer(obj, *args, **kwargs):
obj.ensureAuthenticated() obj.ensureAuthenticated()
try: try:
return func(obj, *args, **kwargs) return func(obj, *args, **kwargs)
except AuthenticationRequiredException:
# Retry funcion with a re-auth
obj._cleanCache()
obj.authPassword()
return func(obj, *args, **kwargs)
except Exception as e: except Exception as e:
logger.error('Got error {} for openstack'.format(e)) logger.error('Got error {} for openstack'.format(e))
obj._cleanCache() # On any request error, force next time auth obj._cleanCache() # On any request error, force next time auth
raise raise
return ensurer return ensurer
def authProjectRequired(func): def authProjectRequired(func):
def ensurer(obj, *args, **kwargs): def ensurer(obj, *args, **kwargs):
if obj._projectId is None: if obj._projectId is None:
raise Exception('Need a project for method {}'.format(func)) raise Exception('Need a project for method {}'.format(func))
obj.ensureAuthenticated() obj.ensureAuthenticated()
try:
return func(obj, *args, **kwargs) return func(obj, *args, **kwargs)
except AuthenticationRequiredException:
# Retry funcion with a re-auth
obj._cleanCache()
obj.authPassword()
return func(obj, *args, **kwargs)
except Exception as e:
logger.error('Got error {} for openstack'.format(e))
obj._cleanCache() # On any request error, force next time auth
raise
return ensurer return ensurer
@@ -237,22 +263,20 @@ class Client(object):
# Extract the token id # Extract the token id
token = r.json()['token'] token = r.json()['token']
self._userId = token['user']['id'] self._userId = token['user']['id']
validity = (dateutil.parser.parse(token['expires_at']).replace(tzinfo=None) - dateutil.parser.parse(token['issued_at']).replace(tzinfo=None)).seconds - 60
# validity = (dateutil.parser.parse(token['expires_at']).replace(tzinfo=None) - dateutil.parser.parse(token['issued_at']).replace(tzinfo=None)).seconds - 60
logger.debug('The token {} will be valid for {}'.format(self._tokenId, validity)) # logger.debug('The token {} will be valid for {}'.format(self._tokenId, validity))
# Now, if endpoints are present (only if tenant was specified), store & cache them # Now, if endpoints are present (only if tenant was specified), store & cache them
if self._projectId is not None: if self._projectId is not None:
self._catalog = token['catalog'] self._catalog = token['catalog']
self._saveToCache(validity) self._saveToCache(300) # Store credentials 300 seconds
def ensureAuthenticated(self): def ensureAuthenticated(self):
if self._authenticated is False: if self._authenticated is False:
self.authPassword() self.authPassword()
@authRequired @authRequired
def listProjects(self): def listProjects(self):
return getRecurringUrlJson(self._authUrl + 'v3/users/{user_id}/projects'.format(user_id=self._userId), return getRecurringUrlJson(self._authUrl + 'v3/users/{user_id}/projects'.format(user_id=self._userId),
@@ -261,7 +285,6 @@ class Client(object):
errMsg='List Projects', errMsg='List Projects',
timeout=self._timeout) timeout=self._timeout)
@authRequired @authRequired
def listRegions(self): def listRegions(self):
return getRecurringUrlJson(self._authUrl + 'v3/regions/', return getRecurringUrlJson(self._authUrl + 'v3/regions/',
@@ -270,7 +293,6 @@ class Client(object):
errMsg='List Regions', errMsg='List Regions',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listServers(self, detail=False, params=None): def listServers(self, detail=False, params=None):
path = '/servers/' + 'detail' if detail is True else '' path = '/servers/' + 'detail' if detail is True else ''
@@ -281,7 +303,6 @@ class Client(object):
errMsg='List Vms', errMsg='List Vms',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listImages(self): def listImages(self):
return getRecurringUrlJson(self._getEndpointFor('image') + '/v2/images?status=active', return getRecurringUrlJson(self._getEndpointFor('image') + '/v2/images?status=active',
@@ -290,7 +311,6 @@ class Client(object):
errMsg='List Images', errMsg='List Images',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listVolumeTypes(self): def listVolumeTypes(self):
return getRecurringUrlJson(self._getEndpointFor('volumev2') + '/types', return getRecurringUrlJson(self._getEndpointFor('volumev2') + '/types',
@@ -299,7 +319,6 @@ class Client(object):
errMsg='List Volume Types', errMsg='List Volume Types',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listVolumes(self): def listVolumes(self):
# self._getEndpointFor('volumev2') + '/volumes' # self._getEndpointFor('volumev2') + '/volumes'
@@ -309,7 +328,6 @@ class Client(object):
errMsg='List Volumes', errMsg='List Volumes',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listVolumeSnapshots(self, volumeId=None): def listVolumeSnapshots(self, volumeId=None):
for s in getRecurringUrlJson(self._getEndpointFor('volumev2') + '/snapshots', for s in getRecurringUrlJson(self._getEndpointFor('volumev2') + '/snapshots',
@@ -320,7 +338,6 @@ class Client(object):
if volumeId is None or s['volume_id'] == volumeId: if volumeId is None or s['volume_id'] == volumeId:
yield s yield s
@authProjectRequired @authProjectRequired
def listAvailabilityZones(self): def listAvailabilityZones(self):
for az in getRecurringUrlJson(self._getEndpointFor('compute') + '/os-availability-zone', for az in getRecurringUrlJson(self._getEndpointFor('compute') + '/os-availability-zone',
@@ -331,7 +348,6 @@ class Client(object):
if az['zoneState']['available'] is True: if az['zoneState']['available'] is True:
yield az['zoneName'] yield az['zoneName']
@authProjectRequired @authProjectRequired
def listFlavors(self): def listFlavors(self):
return getRecurringUrlJson(self._getEndpointFor('compute') + '/flavors', return getRecurringUrlJson(self._getEndpointFor('compute') + '/flavors',
@@ -340,7 +356,6 @@ class Client(object):
errMsg='List Flavors', errMsg='List Flavors',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def listNetworks(self): def listNetworks(self):
return getRecurringUrlJson(self._getEndpointFor('network') + '/v2.0/networks', return getRecurringUrlJson(self._getEndpointFor('network') + '/v2.0/networks',
@@ -372,7 +387,6 @@ class Client(object):
errMsg='List security groups', errMsg='List security groups',
timeout=self._timeout) timeout=self._timeout)
@authProjectRequired @authProjectRequired
def getServer(self, serverId): def getServer(self, serverId):
r = requests.get(self._getEndpointFor('compute') + '/servers/{server_id}'.format(server_id=serverId), r = requests.get(self._getEndpointFor('compute') + '/servers/{server_id}'.format(server_id=serverId),
@@ -396,7 +410,6 @@ class Client(object):
return v return v
@authProjectRequired @authProjectRequired
def getSnapshot(self, snapshotId): def getSnapshot(self, snapshotId):
''' '''
@@ -414,7 +427,6 @@ class Client(object):
return v return v
@authProjectRequired @authProjectRequired
def updateSnapshot(self, snapshotId, name=None, description=None): def updateSnapshot(self, snapshotId, name=None, description=None):
data = { 'snapshot': {} } data = { 'snapshot': {} }
@@ -436,7 +448,6 @@ class Client(object):
return v return v
@authProjectRequired @authProjectRequired
def createVolumeSnapshot(self, volumeId, name, description=None): def createVolumeSnapshot(self, volumeId, name, description=None):
description = 'UDS Snapshot' if description is None else description description = 'UDS Snapshot' if description is None else description
@@ -461,7 +472,6 @@ class Client(object):
return r.json()['snapshot'] return r.json()['snapshot']
@authProjectRequired @authProjectRequired
def createVolumeFromSnapshot(self, snapshotId, name, description=None): def createVolumeFromSnapshot(self, snapshotId, name, description=None):
description = 'UDS Volume' if description is None else description description = 'UDS Volume' if description is None else description
@@ -520,7 +530,6 @@ class Client(object):
return r.json()['server'] return r.json()['server']
@authProjectRequired @authProjectRequired
def deleteServer(self, serverId): def deleteServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId), r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
@@ -533,7 +542,6 @@ class Client(object):
# This does not returns anything # This does not returns anything
@authProjectRequired @authProjectRequired
def deleteSnapshot(self, snapshotId): def deleteSnapshot(self, snapshotId):
r = requests.delete(self._getEndpointFor('volumev2') + '/snapshots/{snapshot_id}'.format(snapshot_id=snapshotId), r = requests.delete(self._getEndpointFor('volumev2') + '/snapshots/{snapshot_id}'.format(snapshot_id=snapshotId),
@@ -545,7 +553,6 @@ class Client(object):
# Does not returns a message body # Does not returns a message body
@authProjectRequired @authProjectRequired
def startServer(self, serverId): def startServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId), r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
@@ -558,7 +565,6 @@ class Client(object):
# This does not returns anything # This does not returns anything
@authProjectRequired @authProjectRequired
def stopServer(self, serverId): def stopServer(self, serverId):
r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId), r = requests.post(self._getEndpointFor('compute') + '/servers/{server_id}/action'.format(server_id=serverId),
@@ -589,7 +595,6 @@ class Client(object):
ensureResponseIsValid(r, 'Resuming server') ensureResponseIsValid(r, 'Resuming server')
def testConnection(self): def testConnection(self):
# First, ensure requested api is supported # First, ensure requested api is supported
# We need api version 3.2 or greater # We need api version 3.2 or greater