# Copyright 2017, Kevin Carter # # Licensed under the Apache License, Version 2.0 (the "License"); # 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. """OpenStack-related utilities.""" import sys try: if sys.version_info > (3, 2, 0): # pragma: no cover import urllib.parse as urlparse else: # pragma: no cover import urlparse except ImportError: # pragma: no cover raise SystemExit('No urlparse module was found.') try: from openstack import connection as os_conn # pragma: no cover except ImportError as e: # pragma: no cover raise SystemExit('OpenStack plugins require access to the OpenStackSDK.' ' Please install "python-openstacksdk".' ' ERROR: %s' % str(e)) from monitorstack import utils class OpenStack(object): """Class for reusable OpenStack utility methods.""" def __init__(self, os_auth_args): """Initialization method for class. :param os_auth_args: dict containing auth creds. :type os_auth_args: dict """ self.os_auth_args = os_auth_args self.verify = self.os_auth_args.get('insecure', True) is False @property def conn(self): """Return an OpenStackSDK connection. :returns: object """ return os_conn.Connection(verify=self.verify, **self.os_auth_args) def _session_req(self, path, service_type, interface='internal'): """Return compute resource limits for a project. :param path: URL path to make a request against. :type path: str :param interface: Interface name, normally [internal, public, admin]. :type interface: str :returns: dict """ endpoint_url = self.conn.session.get_endpoint( interface=interface, service_type=service_type ) sess_url = urlparse.urljoin(endpoint_url, path) return self.conn.session.get(sess_url).json() def get_consumer_usage(self, servers=None, marker=None, limit=512): """Retrieve current usage by an OpenStack cloud consumer. :param servers: ID of a given project to lookup. :type servers: str || uuid :param marker: ID of last server seen. :type marker: str || uuid :param limit: Number of items a single API call can return. :type limit: int :returns: list """ tenant_kwargs = {'details': True, 'all_tenants': True, 'limit': limit} if not servers: servers = list() if marker: tenant_kwargs['marker'] = marker count = 0 for server in self.conn.compute.servers(**tenant_kwargs): servers.append(server.to_dict()) count += 1 if count == limit: return self.get_consumer_usage( servers=servers, marker=servers[-1]['id'] ) return servers def get_compute_limits(self, project_id, interface='internal'): """Return compute resource limits for a project. :param project_id: ID of a given project to lookup. :type project_id: str || uuid :param interface: Interface name, normally [internal, public, admin]. :type interface: str :returns: dict """ path = '/os-quota-sets/' + project_id return self._session_req( path=path, service_type='compute', interface=interface ) def get_projects(self): """Retrieve a list of projects. :returns: list """ _consumers = list() with utils.LocalCache() as c: for project in self.conn.identity.projects(): _consumers.append(project) cache_key = 'projects_' + str(project.id) c.set( cache_key, project.to_dict(), expire=43200, tag='projects' ) return _consumers def get_project(self, project_id): """Retrieve project data. :param project_id: ID of a given project to lookup. :type project_id: str || uuid :returns: dict """ project = None cache_key = 'projects_{}'.format(project_id) with utils.LocalCache() as c: try: project = c.get(cache_key) if not project: raise LookupError except LookupError: project_info = self.conn.identity.get_project(project_id) project = project_info.to_dict() c.set(cache_key, project, expire=43200, tag='projects') finally: return project def get_project_name(self, project_id): """Retrieve the name of a project.""" return self.get_project(project_id=project_id)['name'] def get_flavors(self): """Retrieve all of flavors. :returns: dict """ flavors = dict() with utils.LocalCache() as c: for flavor in self.conn.compute.flavors(): _flavor = flavor.to_dict() cache_key = 'flavor_' + str(flavor.id) c.set( cache_key, _flavor, expire=43200, tag='flavors' ) entry = flavors[flavor.id] = dict() entry.update(_flavor) return flavors def get_flavor(self, flavor_id): """Retrieve a flavor. :param flavor_id: ID of a given flavor to lookup. :type flavor_id: int || str :returns: dict """ flavor = None cache_key = 'flavor_{}'.format(flavor_id) with utils.LocalCache() as c: try: flavor = c.get(cache_key) if not flavor: raise LookupError except LookupError: flavor_info = self.conn.compute.get_flavor(flavor_id) flavor = flavor_info.to_dict() c.set(cache_key, flavor, expire=43200, tag='flavors') finally: return flavor def get_flavor_name(self, flavor_id): """Retrieve the name of a flavor. :param flavor_id: ID of a given flavor to lookup. :type flavor_id: int || str :returns: str """ return self.get_flavor(flavor_id=flavor_id)['name'] def get_volume_pool_stats(self, interface='internal'): """Return volume pool usages. :param interface: Interface name, normally [internal, public, admin]. :type interface: str :returns: dict """ path = '/scheduler-stats/get_pools?detail=True' return self._session_req( path=path, service_type='volume', interface=interface )