--- /dev/null
+#!/usr/bin/python
+
+#
+# Copyright 2016 RIFT.IO Inc
+#
+# 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.
+#
+
+import json
+import logging
+import ipaddress
+
+from keystoneclient import v3 as ksclientv3
+from keystoneclient.v2_0 import client as ksclientv2
+from novaclient import client as nova_client
+from neutronclient.neutron import client as ntclient
+from glanceclient.v2 import client as glclient
+from ceilometerclient import client as ceilo_client
+
+# Exceptions
+import novaclient.exceptions as NovaException
+import keystoneclient.exceptions as KeystoneExceptions
+import neutronclient.common.exceptions as NeutronException
+import glanceclient.exc as GlanceException
+
+logger = logging.getLogger('rwcal.openstack.drv')
+logger.setLevel(logging.DEBUG)
+
+class ValidationError(Exception):
+ pass
+
+
+class KeystoneDriver(object):
+ """
+ Driver base-class for keystoneclient APIs
+ """
+ def __init__(self, ksclient):
+ """
+ Constructor for KeystoneDriver base class
+ Arguments: None
+ Returns: None
+ """
+ self.ksclient = ksclient
+
+ def get_username(self):
+ """
+ Returns the username associated with keystoneclient connection
+ """
+ return self._username
+
+ def get_password(self):
+ """
+ Returns the password associated with keystoneclient connection
+ """
+ return self._password
+
+ def get_tenant_name(self):
+ """
+ Returns the tenant name associated with keystoneclient connection
+ """
+ return self._tenant_name
+
+ def _get_keystone_connection(self):
+ """
+ Returns object of class python-keystoneclient class
+ """
+ if not hasattr(self, '_keystone_connection'):
+ self._keystone_connection = self.ksclient(**self._get_keystone_credentials())
+ return self._keystone_connection
+
+ def is_auth_token_valid(self, token_expiry, time_fmt):
+ """
+ Performs validity on auth_token
+ Arguments:
+ token_expiry (string): Expiry time for token
+ time_fmt (string) : Format for expiry string in auth_ref
+
+ Returns:
+ True/False (Boolean): (auth_token is valid or auth_token is invalid)
+ """
+ import time
+ import datetime
+ import dateutil.parser
+ try:
+ now = datetime.datetime.timetuple(datetime.datetime.utcnow())
+ expires_at = dateutil.parser.parse(token_expiry)
+ t_now = time.mktime(now)
+ t_expiry = time.mktime(expires_at.timetuple())
+
+ if (t_expiry <= t_now) or ((t_expiry - t_now) < 300 ):
+ ### Token has expired or about to expire (5 minute)
+ delattr(self, '_keystone_connection')
+ return False
+ else:
+ return True
+ except Exception as e:
+ logger.error("Received except %s during auth_token validity check" %str(e))
+ logger.info("Can not validate the auth_token. Assuming invalid")
+ return False
+
+
+ def get_service_endpoint(self, service_type, endpoint_type):
+ """
+ Returns requested type of endpoint for requested service type
+ Arguments:
+ service_type (string): Service Type (e.g. computev3, image, network)
+ endpoint_type(string): Endpoint Type (e.g. publicURL,adminURL,internalURL)
+ Returns:
+ service_endpoint(string): Service endpoint string
+ """
+ endpoint_kwargs = {'service_type' : service_type,
+ 'endpoint_type' : endpoint_type}
+ try:
+ ksconn = self._get_keystone_connection()
+ service_endpoint = ksconn.service_catalog.url_for(**endpoint_kwargs)
+ except Exception as e:
+ logger.error("OpenstackDriver: Service Catalog discovery operation failed for service_type: %s, endpoint_type: %s. Exception: %s" %(service_type, endpoint_type, str(e)))
+ raise
+ return service_endpoint
+
+
+ def get_raw_token(self):
+ """
+ Returns a valid raw_auth_token string
+
+ Returns (string): raw_auth_token string
+ """
+ ksconn = self._get_keystone_connection()
+ try:
+ raw_token = ksconn.get_raw_token_from_identity_service(auth_url = self._auth_url,
+ token = self.get_auth_token())
+ except KeystoneExceptions.AuthorizationFailure as e:
+ logger.error("OpenstackDriver: get_raw_token_from_identity_service Failure. Exception: %s" %(str(e)))
+ return None
+
+ except Exception as e:
+ logger.error("OpenstackDriver: Could not retrieve raw_token. Exception: %s" %(str(e)))
+
+ return raw_token
+
+ def get_tenant_id(self):
+ """
+ Returns tenant_id for the project/tenant. Tenant name is provided during
+ class instantiation
+
+ Returns (string): Tenant ID
+ """
+ ksconn = self._get_keystone_connection()
+ return ksconn.tenant_id
+
+ def get_security_mode(self):
+ """
+ Returns certificate_validation policy in case of SSL/TLS connection.
+ This policy is provided during class instantiation
+
+ Returns (boolean):
+ The boolean returned are designed to match the python-client class instantiation ("insecure") value.
+ for nova/neutron/glance/keystone clients
+
+ True: No certificate validation required -- Insecure mode
+ False: Certificate validation required -- Secure mode
+ """
+ return self._insecure
+
+ def tenant_list(self):
+ """
+ Returns list of tenants
+ """
+ pass
+
+ def tenant_create(self, name):
+ """
+ Create a new tenant
+ """
+ pass
+
+ def tenant_delete(self, tenant_id):
+ """
+ Deletes a tenant identified by tenant_id
+ """
+ pass
+
+ def roles_list(self):
+ pass
+
+ def roles_create(self):
+ pass
+
+ def roles_delete(self):
+ pass
+
+class KeystoneDriverV2(KeystoneDriver):
+ """
+ Driver class for keystoneclient V2 APIs
+ """
+ def __init__(self, username, password, auth_url,tenant_name, insecure):
+ """
+ Constructor for KeystoneDriverV3 class
+ Arguments:
+ username (string) : Username
+ password (string) : Password
+ auth_url (string) : Authentication URL
+ tenant_name(string): Tenant Name
+
+ Returns: None
+ """
+ self._username = username
+ self._password = password
+ self._auth_url = auth_url
+ self._tenant_name = tenant_name
+ self._insecure = insecure
+ super(KeystoneDriverV2, self).__init__(ksclientv2.Client)
+
+ def _get_keystone_credentials(self):
+ """
+ Returns the dictionary of kwargs required to instantiate python-keystoneclient class
+ """
+ creds = {}
+ #creds['user_domain'] = self._domain_name
+ creds['username'] = self._username
+ creds['password'] = self._password
+ creds['auth_url'] = self._auth_url
+ creds['tenant_name'] = self._tenant_name
+ creds['insecure'] = self.get_security_mode()
+ return creds
+
+ def get_auth_token(self):
+ """
+ Returns a valid auth_token
+
+ Returns (string): auth_token string
+ """
+ ksconn = self._get_keystone_connection()
+ return ksconn.auth_token
+
+ def is_auth_token_valid(self):
+ """
+ Performs validity on auth_token
+ Arguments:
+
+ Returns:
+ True/False (Boolean): (auth_token is valid or auth_token is invalid)
+ """
+ ksconn = self._get_keystone_connection()
+ result = super(KeystoneDriverV2, self).is_auth_token_valid(ksconn.auth_ref['token']['expires'],
+ "%Y-%m-%dT%H:%M:%SZ")
+ return result
+
+
+class KeystoneDriverV3(KeystoneDriver):
+ """
+ Driver class for keystoneclient V3 APIs
+ """
+ def __init__(self, username, password, auth_url,tenant_name, insecure):
+ """
+ Constructor for KeystoneDriverV3 class
+ Arguments:
+ username (string) : Username
+ password (string) : Password
+ auth_url (string) : Authentication URL
+ tenant_name(string): Tenant Name
+
+ Returns: None
+ """
+ self._username = username
+ self._password = password
+ self._auth_url = auth_url
+ self._tenant_name = tenant_name
+ self._insecure = insecure
+ super(KeystoneDriverV3, self).__init__(ksclientv3.Client)
+
+ def _get_keystone_credentials(self):
+ """
+ Returns the dictionary of kwargs required to instantiate python-keystoneclient class
+ """
+ creds = {}
+ #creds['user_domain'] = self._domain_name
+ creds['username'] = self._username
+ creds['password'] = self._password
+ creds['auth_url'] = self._auth_url
+ creds['project_name'] = self._tenant_name
+ creds['insecure'] = self._insecure
+ return creds
+
+ def get_auth_token(self):
+ """
+ Returns a valid auth_token
+
+ Returns (string): auth_token string
+ """
+ ksconn = self._get_keystone_connection()
+ return ksconn.auth_ref['auth_token']
+
+ def is_auth_token_valid(self):
+ """
+ Performs validity on auth_token
+ Arguments:
+
+ Returns:
+ True/False (Boolean): (auth_token is valid or auth_token is invalid)
+ """
+ ksconn = self._get_keystone_connection()
+ result = super(KeystoneDriverV3, self).is_auth_token_valid(ksconn.auth_ref['expires_at'],
+ "%Y-%m-%dT%H:%M:%S.%fZ")
+ return result
+
+class NovaDriver(object):
+ """
+ Driver for openstack nova_client
+ """
+ def __init__(self, ks_drv, service_name, version):
+ """
+ Constructor for NovaDriver
+ Arguments: KeystoneDriver class object
+ """
+ self.ks_drv = ks_drv
+ self._service_name = service_name
+ self._version = version
+
+ def _get_nova_credentials(self):
+ """
+ Returns a dictionary of kwargs required to instantiate python-novaclient class
+ """
+ creds = {}
+ creds['version'] = self._version
+ creds['bypass_url'] = self.ks_drv.get_service_endpoint(self._service_name, "publicURL")
+ creds['username'] = self.ks_drv.get_username()
+ creds['project_id'] = self.ks_drv.get_tenant_name()
+ creds['auth_token'] = self.ks_drv.get_auth_token()
+ creds['insecure'] = self.ks_drv.get_security_mode()
+ return creds
+
+ def _get_nova_connection(self):
+ """
+ Returns an object of class python-novaclient
+ """
+ if not hasattr(self, '_nova_connection'):
+ self._nova_connection = nova_client.Client(**self._get_nova_credentials())
+ else:
+ # Reinitialize if auth_token is no longer valid
+ if not self.ks_drv.is_auth_token_valid():
+ self._nova_connection = nova_client.Client(**self._get_nova_credentials())
+ return self._nova_connection
+
+ def _flavor_get(self, flavor_id):
+ """
+ Get flavor by flavor_id
+ Arguments:
+ flavor_id(string): UUID of flavor_id
+
+ Returns:
+ dictionary of flavor parameters
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ flavor = nvconn.flavors.get(flavor_id)
+ except Exception as e:
+ logger.info("OpenstackDriver: Did not find flavor with flavor_id : %s. Exception: %s"%(flavor_id, str(e)))
+ raise
+
+ try:
+ extra_specs = flavor.get_keys()
+ except Exception as e:
+ logger.info("OpenstackDriver: Could not get the EPA attributes for flavor with flavor_id : %s. Exception: %s"%(flavor_id, str(e)))
+ raise
+
+ response = flavor.to_dict()
+ assert 'extra_specs' not in response, "Key extra_specs present as flavor attribute"
+ response['extra_specs'] = extra_specs
+ return response
+
+ def flavor_get(self, flavor_id):
+ """
+ Get flavor by flavor_id
+ Arguments:
+ flavor_id(string): UUID of flavor_id
+
+ Returns:
+ dictionary of flavor parameters
+ """
+ return self._flavor_get(flavor_id)
+
+ def flavor_list(self):
+ """
+ Returns list of all flavors (dictionary per flavor)
+
+ Arguments:
+ None
+ Returns:
+ A list of dictionaries. Each dictionary contains attributes for a single flavor instance
+ """
+ flavors = []
+ flavor_info = []
+ nvconn = self._get_nova_connection()
+ try:
+ flavors = nvconn.flavors.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: List Flavor operation failed. Exception: %s"%(str(e)))
+ raise
+ if flavors:
+ flavor_info = [ self.flavor_get(flv.id) for flv in flavors ]
+ return flavor_info
+
+ def flavor_create(self, name, ram, vcpu, disk, extra_specs):
+ """
+ Create a new flavor
+
+ Arguments:
+ name (string): Name of the new flavor
+ ram (int) : Memory in MB
+ vcpus (int) : Number of VCPUs
+ disk (int) : Secondary storage size in GB
+ extra_specs (dictionary): EPA attributes dictionary
+
+ Returns:
+ flavor_id (string): UUID of flavor created
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ flavor = nvconn.flavors.create(name = name,
+ ram = ram,
+ vcpus = vcpu,
+ disk = disk,
+ flavorid = 'auto',
+ ephemeral = 0,
+ swap = 0,
+ rxtx_factor = 1.0,
+ is_public = True)
+ except Exception as e:
+ logger.error("OpenstackDriver: Create Flavor operation failed. Exception: %s"%(str(e)))
+ raise
+
+ if extra_specs:
+ try:
+ flavor.set_keys(extra_specs)
+ except Exception as e:
+ logger.error("OpenstackDriver: Set Key operation failed for flavor: %s. Exception: %s" %(flavor.id, str(e)))
+ raise
+ return flavor.id
+
+ def flavor_delete(self, flavor_id):
+ """
+ Deletes a flavor identified by flavor_id
+
+ Arguments:
+ flavor_id (string): UUID of flavor to be deleted
+
+ Returns: None
+ """
+ assert flavor_id == self._flavor_get(flavor_id)['id']
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.flavors.delete(flavor_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Delete flavor operation failed for flavor: %s. Exception: %s" %(flavor_id, str(e)))
+ raise
+
+
+ def server_list(self):
+ """
+ Returns a list of available VMs for the project
+
+ Arguments: None
+
+ Returns:
+ A list of dictionaries. Each dictionary contains attributes associated
+ with individual VM
+ """
+ servers = []
+ server_info = []
+ nvconn = self._get_nova_connection()
+ try:
+ servers = nvconn.servers.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: List Server operation failed. Exception: %s" %(str(e)))
+ raise
+ server_info = [ server.to_dict() for server in servers]
+ return server_info
+
+ def _nova_server_get(self, server_id):
+ """
+ Returns a dictionary of attributes associated with VM identified by service_id
+
+ Arguments:
+ server_id (string): UUID of the VM/server for which information is requested
+
+ Returns:
+ A dictionary object with attributes associated with VM identified by server_id
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ server = nvconn.servers.get(server = server_id)
+ except Exception as e:
+ logger.info("OpenstackDriver: Get Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
+ raise
+ else:
+ return server.to_dict()
+
+ def server_get(self, server_id):
+ """
+ Returns a dictionary of attributes associated with VM identified by service_id
+
+ Arguments:
+ server_id (string): UUID of the VM/server for which information is requested
+
+ Returns:
+ A dictionary object with attributes associated with VM identified by server_id
+ """
+ return self._nova_server_get(server_id)
+
+ def server_create(self, **kwargs):
+ """
+ Creates a new VM/server instance
+
+ Arguments:
+ A dictionary of following key-value pairs
+ {
+ server_name(string) : Name of the VM/Server
+ flavor_id (string) : UUID of the flavor to be used for VM
+ image_id (string) : UUID of the image to be used VM/Server instance
+ network_list(List) : A List of network_ids. A port will be created in these networks
+ port_list (List) : A List of port-ids. These ports will be added to VM.
+ metadata (dict) : A dictionary of arbitrary key-value pairs associated with VM/server
+ userdata (string) : A script which shall be executed during first boot of the VM
+ availability_zone (string) : A name of the availability zone where instance should be launched
+ scheduler_hints (string) : Openstack scheduler_hints to be passed to nova scheduler
+ }
+ Returns:
+ server_id (string): UUID of the VM/server created
+
+ """
+ nics = []
+ if 'network_list' in kwargs:
+ for network_id in kwargs['network_list']:
+ nics.append({'net-id': network_id})
+
+ if 'port_list' in kwargs:
+ for port_id in kwargs['port_list']:
+ nics.append({'port-id': port_id})
+
+ nvconn = self._get_nova_connection()
+
+ try:
+ server = nvconn.servers.create(kwargs['name'],
+ kwargs['image_id'],
+ kwargs['flavor_id'],
+ meta = kwargs['metadata'],
+ files = None,
+ reservation_id = None,
+ min_count = None,
+ max_count = None,
+ userdata = kwargs['userdata'],
+ security_groups = kwargs['security_groups'],
+ availability_zone = kwargs['availability_zone'],
+ block_device_mapping = None,
+ nics = nics,
+ scheduler_hints = kwargs['scheduler_hints'],
+ config_drive = None)
+ except Exception as e:
+ logger.info("OpenstackDriver: Create Server operation failed. Exception: %s" %(str(e)))
+ raise
+ return server.to_dict()['id']
+
+ def server_delete(self, server_id):
+ """
+ Deletes a server identified by server_id
+
+ Arguments:
+ server_id (string): UUID of the server to be deleted
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.delete(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Delete server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_start(self, server_id):
+ """
+ Starts a server identified by server_id
+
+ Arguments:
+ server_id (string): UUID of the server to be started
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.start(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Start Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_stop(self, server_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server to be stopped
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.stop(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Stop Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_pause(self, server_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server to be paused
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.pause(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Pause Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_unpause(self, server_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server to be unpaused
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.unpause(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Resume Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+
+ def server_suspend(self, server_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server to be suspended
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.suspend(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Suspend Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+
+
+ def server_resume(self, server_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server to be resumed
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.resume(server_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Resume Server operation failed for server_id : %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_reboot(self, server_id, reboot_type):
+ """
+ Arguments:
+ server_id (string) : UUID of the server to be rebooted
+ reboot_type(string):
+ 'SOFT': Soft Reboot
+ 'HARD': Hard Reboot
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.reboot(server_id, reboot_type)
+ except Exception as e:
+ logger.error("OpenstackDriver: Reboot Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+ def server_console(self, server_id, console_type = 'novnc'):
+ """
+ Arguments:
+ server_id (string) : UUID of the server to be rebooted
+ console_type(string):
+ 'novnc',
+ 'xvpvnc'
+ Returns:
+ A dictionary object response for console information
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ console_info = nvconn.servers.get_vnc_console(server_id, console_type)
+ except Exception as e:
+ logger.error("OpenstackDriver: Server Get-Console operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
+ raise
+ return console_info
+
+ def server_rebuild(self, server_id, image_id):
+ """
+ Arguments:
+ server_id (string) : UUID of the server to be rebooted
+ image_id (string) : UUID of the image to use
+ Returns: None
+ """
+
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.rebuild(server_id, image_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Rebuild Server operation failed for server_id: %s. Exception: %s" %(server_id, str(e)))
+ raise
+
+
+ def server_add_port(self, server_id, port_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server
+ port_id (string): UUID of the port to be attached
+
+ Returns: None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.interface_attach(server_id,
+ port_id,
+ net_id = None,
+ fixed_ip = None)
+ except Exception as e:
+ logger.error("OpenstackDriver: Server Port Add operation failed for server_id : %s, port_id : %s. Exception: %s" %(server_id, port_id, str(e)))
+ raise
+
+ def server_delete_port(self, server_id, port_id):
+ """
+ Arguments:
+ server_id (string): UUID of the server
+ port_id (string): UUID of the port to be deleted
+ Returns: None
+
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.interface_detach(server_id, port_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Server Port Delete operation failed for server_id : %s, port_id : %s. Exception: %s" %(server_id, port_id, str(e)))
+ raise
+
+ def floating_ip_list(self):
+ """
+ Arguments:
+ None
+ Returns:
+ List of objects of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ ip_list = nvconn.floating_ips.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: Floating IP List operation failed. Exception: %s" %str(e))
+ raise
+
+ return ip_list
+
+ def floating_ip_create(self, pool):
+ """
+ Arguments:
+ pool (string): Name of the pool (optional)
+ Returns:
+ An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ floating_ip = nvconn.floating_ips.create(pool)
+ except Exception as e:
+ logger.error("OpenstackDriver: Floating IP Create operation failed. Exception: %s" %str(e))
+ raise
+
+ return floating_ip
+
+ def floating_ip_delete(self, floating_ip):
+ """
+ Arguments:
+ floating_ip: An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
+ Returns:
+ None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ floating_ip = nvconn.floating_ips.delete(floating_ip)
+ except Exception as e:
+ logger.error("OpenstackDriver: Floating IP Delete operation failed. Exception: %s" %str(e))
+ raise
+
+ def floating_ip_assign(self, server_id, floating_ip, fixed_ip):
+ """
+ Arguments:
+ server_id (string) : UUID of the server
+ floating_ip (string): IP address string for floating-ip
+ fixed_ip (string) : IP address string for the fixed-ip with which floating ip will be associated
+ Returns:
+ None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.add_floating_ip(server_id, floating_ip, fixed_ip)
+ except Exception as e:
+ logger.error("OpenstackDriver: Assign Floating IP operation failed. Exception: %s" %str(e))
+ raise
+
+ def floating_ip_release(self, server_id, floating_ip):
+ """
+ Arguments:
+ server_id (string) : UUID of the server
+ floating_ip (string): IP address string for floating-ip
+ Returns:
+ None
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ nvconn.servers.remove_floating_ip(server_id, floating_ip)
+ except Exception as e:
+ logger.error("OpenstackDriver: Release Floating IP operation failed. Exception: %s" %str(e))
+ raise
+
+ def group_list(self):
+ """
+ List of Server Affinity and Anti-Affinity Groups
+
+ Arguments:
+ None
+ Returns:
+ List of dictionary objects where dictionary is representation of class (novaclient.v2.server_groups.ServerGroup)
+ """
+ nvconn = self._get_nova_connection()
+ try:
+ group_list = nvconn.server_groups.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: Server Group List operation failed. Exception: %s" %str(e))
+ raise
+
+ group_info = [ group.to_dict() for group in group_list ]
+ return group_info
+
+
+
+class NovaDriverV2(NovaDriver):
+ """
+ Driver class for novaclient V2 APIs
+ """
+ def __init__(self, ks_drv):
+ """
+ Constructor for NovaDriver
+ Arguments: KeystoneDriver class object
+ """
+ super(NovaDriverV2, self).__init__(ks_drv, 'compute', '2.0')
+
+class NovaDriverV21(NovaDriver):
+ """
+ Driver class for novaclient V2 APIs
+ """
+ def __init__(self, ks_drv):
+ """
+ Constructor for NovaDriver
+ Arguments: KeystoneDriver class object
+ """
+ super(NovaDriverV21, self).__init__(ks_drv, 'computev21', '2.1')
+
+class GlanceDriver(object):
+ """
+ Driver for openstack glance-client
+ """
+ def __init__(self, ks_drv, service_name, version):
+ """
+ Constructor for GlanceDriver
+ Arguments: KeystoneDriver class object
+ """
+ self.ks_drv = ks_drv
+ self._service_name = service_name
+ self._version = version
+
+ def _get_glance_credentials(self):
+ """
+ Returns a dictionary of kwargs required to instantiate python-glanceclient class
+
+ Arguments: None
+
+ Returns:
+ A dictionary object of arguments
+ """
+ creds = {}
+ creds['version'] = self._version
+ creds['endpoint'] = self.ks_drv.get_service_endpoint(self._service_name, 'publicURL')
+ creds['token'] = self.ks_drv.get_auth_token()
+ creds['insecure'] = self.ks_drv.get_security_mode()
+ return creds
+
+ def _get_glance_connection(self):
+ """
+ Returns a object of class python-glanceclient
+ """
+ if not hasattr(self, '_glance_connection'):
+ self._glance_connection = glclient.Client(**self._get_glance_credentials())
+ else:
+ # Reinitialize if auth_token is no longer valid
+ if not self.ks_drv.is_auth_token_valid():
+ self._glance_connection = glclient.Client(**self._get_glance_credentials())
+ return self._glance_connection
+
+ def image_list(self):
+ """
+ Returns list of dictionaries. Each dictionary contains attributes associated with
+ image
+
+ Arguments: None
+
+ Returns: List of dictionaries.
+ """
+ glconn = self._get_glance_connection()
+ images = []
+ try:
+ image_info = glconn.images.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: List Image operation failed. Exception: %s" %(str(e)))
+ raise
+ images = [ img for img in image_info ]
+ return images
+
+ def image_create(self, **kwargs):
+ """
+ Creates an image
+ Arguments:
+ A dictionary of kwargs with following keys
+ {
+ 'name'(string) : Name of the image
+ 'location'(string) : URL (http://....) where image is located
+ 'disk_format'(string) : Disk format
+ Possible values are 'ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso'
+ 'container_format'(string): Container format
+ Possible values are 'ami', 'ari', 'aki', 'bare', 'ovf'
+ 'tags' : A list of user tags
+ 'checksum' : The image md5 checksum
+ }
+ Returns:
+ image_id (string) : UUID of the image
+
+ """
+ glconn = self._get_glance_connection()
+ try:
+ image = glconn.images.create(**kwargs)
+ except Exception as e:
+ logger.error("OpenstackDriver: Create Image operation failed. Exception: %s" %(str(e)))
+ raise
+
+ return image.id
+
+ def image_upload(self, image_id, fd):
+ """
+ Upload the image
+
+ Arguments:
+ image_id: UUID of the image
+ fd : File descriptor for the image file
+ Returns: None
+ """
+ glconn = self._get_glance_connection()
+ try:
+ glconn.images.upload(image_id, fd)
+ except Exception as e:
+ logger.error("OpenstackDriver: Image upload operation failed. Exception: %s" %(str(e)))
+ raise
+
+ def image_add_location(self, image_id, location, metadata):
+ """
+ Add image URL location
+
+ Arguments:
+ image_id : UUID of the image
+ location : http URL for the image
+
+ Returns: None
+ """
+ glconn = self._get_glance_connection()
+ try:
+ image = glconn.images.add_location(image_id, location, metadata)
+ except Exception as e:
+ logger.error("OpenstackDriver: Image location add operation failed. Exception: %s" %(str(e)))
+ raise
+
+ def image_update(self):
+ pass
+
+ def image_delete(self, image_id):
+ """
+ Delete an image
+
+ Arguments:
+ image_id: UUID of the image
+
+ Returns: None
+
+ """
+ assert image_id == self._image_get(image_id)['id']
+ glconn = self._get_glance_connection()
+ try:
+ glconn.images.delete(image_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Delete Image operation failed for image_id : %s. Exception: %s" %(image_id, str(e)))
+ raise
+
+
+ def _image_get(self, image_id):
+ """
+ Returns a dictionary object of VM image attributes
+
+ Arguments:
+ image_id (string): UUID of the image
+
+ Returns:
+ A dictionary of the image attributes
+ """
+ glconn = self._get_glance_connection()
+ try:
+ image = glconn.images.get(image_id)
+ except GlanceException.HTTPBadRequest:
+ # RIFT-14241: The get image request occasionally returns the below message. Retry in case of bad request exception.
+ # Error code 400.: Message: Bad request syntax ('0').: Error code explanation: 400 = Bad request syntax or unsupported method. (HTTP 400)
+ logger.warning("OpenstackDriver: Got bad request response during get_image request. Retrying.")
+ image = glconn.images.get(image_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Get Image operation failed for image_id : %s. Exception: %s" %(image_id, str(e)))
+ raise
+
+ return image
+
+ def image_get(self, image_id):
+ """
+ Returns a dictionary object of VM image attributes
+
+ Arguments:
+ image_id (string): UUID of the image
+
+ Returns:
+ A dictionary of the image attributes
+ """
+ return self._image_get(image_id)
+
+class GlanceDriverV2(GlanceDriver):
+ """
+ Driver for openstack glance-client V2
+ """
+ def __init__(self, ks_drv):
+ super(GlanceDriverV2, self).__init__(ks_drv, 'image', 2)
+
+class NeutronDriver(object):
+ """
+ Driver for openstack neutron neutron-client
+ """
+ def __init__(self, ks_drv, service_name, version):
+ """
+ Constructor for NeutronDriver
+ Arguments: KeystoneDriver class object
+ """
+ self.ks_drv = ks_drv
+ self._service_name = service_name
+ self._version = version
+
+ def _get_neutron_credentials(self):
+ """
+ Returns a dictionary of kwargs required to instantiate python-neutronclient class
+
+ Returns:
+ Dictionary of kwargs
+ """
+ creds = {}
+ creds['api_version'] = self._version
+ creds['endpoint_url'] = self.ks_drv.get_service_endpoint(self._service_name, 'publicURL')
+ creds['token'] = self.ks_drv.get_auth_token()
+ creds['tenant_name'] = self.ks_drv.get_tenant_name()
+ creds['insecure'] = self.ks_drv.get_security_mode()
+ return creds
+
+ def _get_neutron_connection(self):
+ """
+ Returns an object of class python-neutronclient
+ """
+ if not hasattr(self, '_neutron_connection'):
+ self._neutron_connection = ntclient.Client(**self._get_neutron_credentials())
+ else:
+ # Reinitialize if auth_token is no longer valid
+ if not self.ks_drv.is_auth_token_valid():
+ self._neutron_connection = ntclient.Client(**self._get_neutron_credentials())
+ return self._neutron_connection
+
+ def network_list(self):
+ """
+ Returns list of dictionaries. Each dictionary contains the attributes for a network
+ under project
+
+ Arguments: None
+
+ Returns:
+ A list of dictionaries
+ """
+ networks = []
+ ntconn = self._get_neutron_connection()
+ try:
+ networks = ntconn.list_networks()
+ except Exception as e:
+ logger.error("OpenstackDriver: List Network operation failed. Exception: %s" %(str(e)))
+ raise
+ return networks['networks']
+
+ def network_create(self, **kwargs):
+ """
+ Creates a new network for the project
+
+ Arguments:
+ A dictionary with following key-values
+ {
+ name (string) : Name of the network
+ admin_state_up(Boolean) : True/False (Defaults: True)
+ external_router(Boolean) : Connectivity with external router. True/False (Defaults: False)
+ shared(Boolean) : Shared among tenants. True/False (Defaults: False)
+ physical_network(string) : The physical network where this network object is implemented (optional).
+ network_type : The type of physical network that maps to this network resource (optional).
+ Possible values are: 'flat', 'vlan', 'vxlan', 'gre'
+ segmentation_id : An isolated segment on the physical network. The network_type attribute
+ defines the segmentation model. For example, if the network_type value
+ is vlan, this ID is a vlan identifier. If the network_type value is gre,
+ this ID is a gre key.
+ }
+ """
+ params = {'network':
+ {'name' : kwargs['name'],
+ 'admin_state_up' : kwargs['admin_state_up'],
+ 'tenant_id' : self.ks_drv.get_tenant_id(),
+ 'shared' : kwargs['shared'],
+ #'port_security_enabled': port_security_enabled,
+ 'router:external' : kwargs['external_router']}}
+
+ if 'physical_network' in kwargs:
+ params['network']['provider:physical_network'] = kwargs['physical_network']
+ if 'network_type' in kwargs:
+ params['network']['provider:network_type'] = kwargs['network_type']
+ if 'segmentation_id' in kwargs:
+ params['network']['provider:segmentation_id'] = kwargs['segmentation_id']
+
+ ntconn = self._get_neutron_connection()
+ try:
+ logger.debug("Calling neutron create_network() with params: %s", str(params))
+ net = ntconn.create_network(params)
+ except Exception as e:
+ logger.error("OpenstackDriver: Create Network operation failed. Exception: %s" %(str(e)))
+ raise
+ logger.debug("Got create_network response from neutron connection: %s", str(net))
+ network_id = net['network']['id']
+ if not network_id:
+ raise Exception("Empty network id returned from create_network. (params: %s)" % str(params))
+
+ return network_id
+
+ def network_delete(self, network_id):
+ """
+ Deletes a network identified by network_id
+
+ Arguments:
+ network_id (string): UUID of the network
+
+ Returns: None
+ """
+ assert network_id == self._network_get(network_id)['id']
+ ntconn = self._get_neutron_connection()
+ try:
+ ntconn.delete_network(network_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Delete Network operation failed. Exception: %s" %(str(e)))
+ raise
+
+ def _network_get(self, network_id):
+ """
+ Returns a dictionary object describing the attributes of the network
+
+ Arguments:
+ network_id (string): UUID of the network
+
+ Returns:
+ A dictionary object of the network attributes
+ """
+ ntconn = self._get_neutron_connection()
+ network = ntconn.list_networks(id = network_id)['networks']
+ if not network:
+ raise NeutronException.NotFound("Network with id %s not found"%(network_id))
+
+ return network[0]
+
+ def network_get(self, network_id):
+ """
+ Returns a dictionary object describing the attributes of the network
+
+ Arguments:
+ network_id (string): UUID of the network
+
+ Returns:
+ A dictionary object of the network attributes
+ """
+ return self._network_get(network_id)
+
+ def subnet_create(self, **kwargs):
+ """
+ Creates a subnet on the network
+
+ Arguments:
+ A dictionary with following key value pairs
+ {
+ network_id(string) : UUID of the network where subnet needs to be created
+ subnet_cidr(string) : IPv4 address prefix (e.g. '1.1.1.0/24') for the subnet
+ ip_version (integer): 4 for IPv4 and 6 for IPv6
+
+ }
+
+ Returns:
+ subnet_id (string): UUID of the created subnet
+ """
+ params = {}
+ params['network_id'] = kwargs['network_id']
+ params['ip_version'] = kwargs['ip_version']
+
+ # if params['ip_version'] == 6:
+ # assert 0, "IPv6 is not supported"
+
+ if 'subnetpool_id' in kwargs:
+ params['subnetpool_id'] = kwargs['subnetpool_id']
+ else:
+ params['cidr'] = kwargs['cidr']
+
+ if 'gateway_ip' in kwargs:
+ params['gateway_ip'] = kwargs['gateway_ip']
+ else:
+ params['gateway_ip'] = None
+
+ if 'dhcp_params' in kwargs:
+ params['enable_dhcp'] = kwargs['dhcp_params']['enable_dhcp']
+ if 'start_address' in kwargs['dhcp_params'] and 'count' in kwargs['dhcp_params']:
+ end_address = (ipaddress.IPv4Address(kwargs['dhcp_params']['start_address']) + kwargs['dhcp_params']['count']).compressed
+ params['allocation_pools'] = [ {'start': kwargs['dhcp_params']['start_address'] ,
+ 'end' : end_address} ]
+
+ if 'dns_server' in kwargs:
+ params['dns_nameservers'] = []
+ for server in kwargs['dns_server']:
+ params['dns_nameservers'].append(server)
+
+ ntconn = self._get_neutron_connection()
+ try:
+ subnet = ntconn.create_subnet({'subnets': [params]})
+ except Exception as e:
+ logger.error("OpenstackDriver: Create Subnet operation failed. Exception: %s" %(str(e)))
+ raise
+
+ return subnet['subnets'][0]['id']
+
+ def subnet_list(self):
+ """
+ Returns a list of dictionaries. Each dictionary contains attributes describing the subnet
+
+ Arguments: None
+
+ Returns:
+ A dictionary of the objects of subnet attributes
+ """
+ ntconn = self._get_neutron_connection()
+ try:
+ subnets = ntconn.list_subnets()['subnets']
+ except Exception as e:
+ logger.error("OpenstackDriver: List Subnet operation failed. Exception: %s" %(str(e)))
+ raise
+ return subnets
+
+ def _subnet_get(self, subnet_id):
+ """
+ Returns a dictionary object describing the attributes of a subnet.
+
+ Arguments:
+ subnet_id (string): UUID of the subnet
+
+ Returns:
+ A dictionary object of the subnet attributes
+ """
+ ntconn = self._get_neutron_connection()
+ subnets = ntconn.list_subnets(id=subnet_id)
+ if not subnets['subnets']:
+ logger.error("OpenstackDriver: Get subnet operation failed for subnet_id: %s" %(subnet_id))
+ #raise NeutronException.NotFound("Could not find subnet_id %s" %(subnet_id))
+ return {'cidr': ''}
+ else:
+ return subnets['subnets'][0]
+
+ def subnet_get(self, subnet_id):
+ """
+ Returns a dictionary object describing the attributes of a subnet.
+
+ Arguments:
+ subnet_id (string): UUID of the subnet
+
+ Returns:
+ A dictionary object of the subnet attributes
+ """
+ return self._subnet_get(subnet_id)
+
+ def subnet_delete(self, subnet_id):
+ """
+ Deletes a subnet identified by subnet_id
+
+ Arguments:
+ subnet_id (string): UUID of the subnet to be deleted
+
+ Returns: None
+ """
+ ntconn = self._get_neutron_connection()
+ assert subnet_id == self._subnet_get(self,subnet_id)
+ try:
+ ntconn.delete_subnet(subnet_id)
+ except Exception as e:
+ logger.error("OpenstackDriver: Delete Subnet operation failed for subnet_id : %s. Exception: %s" %(subnet_id, str(e)))
+ raise
+
+ def port_list(self, **kwargs):
+ """
+ Returns a list of dictionaries. Each dictionary contains attributes describing the port
+
+ Arguments:
+ kwargs (dictionary): A dictionary for filters for port_list operation
+
+ Returns:
+ A dictionary of the objects of port attributes
+
+ """
+ ports = []
+ ntconn = self._get_neutron_connection()
+
+ kwargs['tenant_id'] = self.ks_drv.get_tenant_id()
+
+ try:
+ ports = ntconn.list_ports(**kwargs)
+ except Exception as e:
+ logger.info("OpenstackDriver: List Port operation failed. Exception: %s" %(str(e)))
+ raise
+ return ports['ports']
+
+ def port_create(self, **kwargs):
+ """
+ Create a port in network
+
+ Arguments:
+ A dictionary of following
+ {
+ name (string) : Name of the port
+ network_id(string) : UUID of the network_id identifying the network to which port belongs
+ subnet_id(string) : UUID of the subnet_id from which IP-address will be assigned to port
+ vnic_type(string) : Possible values are "normal", "direct", "macvtap"
+ }
+ Returns:
+ port_id (string) : UUID of the port
+ """
+ params = {
+ "port": {
+ "admin_state_up" : kwargs['admin_state_up'],
+ "name" : kwargs['name'],
+ "network_id" : kwargs['network_id'],
+ "fixed_ips" : [ {"subnet_id": kwargs['subnet_id']}],
+ "binding:vnic_type" : kwargs['port_type']}}
+
+ ntconn = self._get_neutron_connection()
+ try:
+ port = ntconn.create_port(params)
+ except Exception as e:
+ logger.error("OpenstackDriver: Port Create operation failed. Exception: %s" %(str(e)))
+ raise
+ return port['port']['id']
+
+ def _port_get(self, port_id):
+ """
+ Returns a dictionary object describing the attributes of the port
+
+ Arguments:
+ port_id (string): UUID of the port
+
+ Returns:
+ A dictionary object of the port attributes
+ """
+ ntconn = self._get_neutron_connection()
+ port = ntconn.list_ports(id=port_id)['ports']
+ if not port:
+ raise NeutronException.NotFound("Could not find port_id %s" %(port_id))
+ return port[0]
+
+ def port_get(self, port_id):
+ """
+ Returns a dictionary object describing the attributes of the port
+
+ Arguments:
+ port_id (string): UUID of the port
+
+ Returns:
+ A dictionary object of the port attributes
+ """
+ return self._port_get(port_id)
+
+ def port_delete(self, port_id):
+ """
+ Deletes a port identified by port_id
+
+ Arguments:
+ port_id (string) : UUID of the port
+
+ Returns: None
+ """
+ assert port_id == self._port_get(port_id)['id']
+ ntconn = self._get_neutron_connection()
+ try:
+ ntconn.delete_port(port_id)
+ except Exception as e:
+ logger.error("Port Delete operation failed for port_id : %s. Exception: %s" %(port_id, str(e)))
+ raise
+
+ def security_group_list(self):
+ """
+ Returns a list of dictionaries. Each dictionary contains attributes describing the security group
+
+ Arguments:
+ None
+
+ Returns:
+ A dictionary of the objects of security group attributes
+ """
+ ntconn = self._get_neutron_connection()
+ try:
+ group_list = ntconn.list_security_groups(tenant_id=self.ks_drv.get_tenant_id())
+ except Exception as e:
+ logger.error("List Security group operation, Exception: %s" %(str(e)))
+ raise
+
+ if 'security_groups' in group_list:
+ return group_list['security_groups']
+ else:
+ return []
+
+ def subnetpool_list(self, **kwargs):
+ """
+ Returns a list of dictionaries. Each dictionary contains attributes describing a subnet prefix pool
+
+ Arguments:
+ None
+
+ Returns:
+ A dictionary of the objects of subnet prefix pool
+ """
+ ntconn = self._get_neutron_connection()
+ try:
+ pool_list = ntconn.list_subnetpools(**kwargs)
+ except Exception as e:
+ logger.error("List SubnetPool operation, Exception: %s" %(str(e)))
+ raise
+
+ if 'subnetpools' in pool_list:
+ return pool_list['subnetpools']
+ else:
+ return []
+
+class NeutronDriverV2(NeutronDriver):
+ """
+ Driver for openstack neutron neutron-client v2
+ """
+ def __init__(self, ks_drv):
+ """
+ Constructor for NeutronDriver
+ Arguments: KeystoneDriver class object
+ """
+ super(NeutronDriverV2, self).__init__(ks_drv, 'network', '2.0')
+
+
+
+class CeilometerDriver(object):
+ """
+ Driver for openstack ceilometer client
+ """
+
+ def __init__(self, ks_drv, service_name, version):
+ """
+ Constructor for CeilometerDriver
+ Arguments: KeystoneDriver class object
+ """
+ self.ks_drv = ks_drv
+ self._service_name = service_name
+ self._version = version
+ self._client = None
+
+ @property
+ def version(self):
+ """The version of the ceilometer client used by the driver"""
+ return self._version
+
+ @property
+ def client(self):
+ """The instance of ceilometer client used by the driver"""
+ if self._client is None or not self.ks_drv.is_auth_token_valid():
+ self._client = ceilo_client.Client(**self.credentials)
+
+ return self._client
+
+ @property
+ def auth_token(self):
+ """The authorization token for the ceilometer client"""
+ try:
+ return self.ks_drv.get_auth_token()
+ except KeystoneExceptions.EndpointNotFound as e:
+ logger.error("OpenstackDriver: unable to get authorization token for ceilometer. Exception: %s" %(str(e)))
+ raise
+
+ @property
+ def security_mode(self):
+ """The security mode for the ceilometer client"""
+ try:
+ return self.ks_drv.get_security_mode()
+ except KeystoneExceptions.EndpointNotFound as e:
+ logger.error("OpenstackDriver: unable to get security mode for ceilometer. Exception: %s" %(str(e)))
+ raise
+
+ @property
+ def endpoint(self):
+ """The service endpoint for the ceilometer client"""
+ try:
+ return self.ks_drv.get_service_endpoint(self._service_name, "publicURL")
+ except KeystoneExceptions.EndpointNotFound as e:
+ logger.error("OpenstackDriver: unable to get endpoint for ceilometer. Exception: %s" %(str(e)))
+ raise
+
+ @property
+ def credentials(self):
+ """A dictionary of credentials for the ceilometer client"""
+ return dict(
+ version=self.version,
+ endpoint=self.endpoint,
+ token=self.auth_token,
+ insecure=self.security_mode,
+ )
+
+ @property
+ def meters(self):
+ """A list of the available meters"""
+ try:
+ return self.client.meters.list()
+ except Exception as e:
+ logger.error("OpenstackDriver: List meters operation failed. Exception: %s" %(str(e)))
+ raise
+
+ @property
+ def alarms(self):
+ """The ceilometer client alarms manager"""
+ return self.client.alarms
+
+ def query_samples(self, vim_instance_id, counter_name, limit=1):
+ """Returns a list of samples
+
+ Arguments:
+ vim_instance_id - the ID of the VIM that the samples are from
+ counter_name - the counter that the samples will come from
+ limit - a limit on the number of samples to return
+ (default: 1)
+
+ Returns:
+ A list of samples
+
+ """
+ try:
+ filter = json.dumps({
+ "and": [
+ {"=": {"resource": vim_instance_id}},
+ {"=": {"counter_name": counter_name}}
+ ]
+ })
+ result = self.client.query_samples.query(filter=filter, limit=limit)
+ return result[-limit:]
+
+ except Exception as e:
+ logger.exception(e)
+
+ return []
+
+
+class CeilometerDriverV2(CeilometerDriver):
+ """
+ Driver for openstack ceilometer ceilometer-client
+ """
+ def __init__(self, ks_drv):
+ """
+ Constructor for CeilometerDriver
+ Arguments: CeilometerDriver class object
+ """
+ super(CeilometerDriverV2, self).__init__(ks_drv, 'metering', '2')
+
+class OpenstackDriver(object):
+ """
+ Driver for openstack nova, neutron, glance, keystone, swift, cinder services
+ """
+ def __init__(self, username, password, auth_url, tenant_name, mgmt_network = None, cert_validate = False):
+ """
+ OpenstackDriver Driver constructor
+ Arguments:
+ username (string) : Username for project/tenant.
+ password (string) : Password
+ auth_url (string) : Keystone Authentication URL.
+ tenant_name (string) : Openstack project name
+ mgmt_network(string, optional) : Management network name. Each VM created with this cloud-account will
+ have a default interface into management network.
+ cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
+
+ """
+ insecure = not cert_validate
+ if auth_url.find('/v3') != -1:
+ self.ks_drv = KeystoneDriverV3(username, password, auth_url, tenant_name, insecure)
+ self.glance_drv = GlanceDriverV2(self.ks_drv)
+ self.nova_drv = NovaDriverV21(self.ks_drv)
+ self.neutron_drv = NeutronDriverV2(self.ks_drv)
+ self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
+ elif auth_url.find('/v2') != -1:
+ self.ks_drv = KeystoneDriverV2(username, password, auth_url, tenant_name, insecure)
+ self.glance_drv = GlanceDriverV2(self.ks_drv)
+ self.nova_drv = NovaDriverV2(self.ks_drv)
+ self.neutron_drv = NeutronDriverV2(self.ks_drv)
+ self.ceilo_drv = CeilometerDriverV2(self.ks_drv)
+ else:
+ logger.error("Could not identity the version information for openstack service endpoints. Auth_URL should contain \"/v2\" or \"/v3\" string in it")
+ raise NotImplementedError("Auth URL is wrong or invalid. Only Keystone v2 & v3 supported")
+
+ if mgmt_network != None:
+ self._mgmt_network = mgmt_network
+
+ networks = []
+ try:
+ ntconn = self.neutron_drv._get_neutron_connection()
+ networks = ntconn.list_networks()
+ except Exception as e:
+ logger.error("OpenstackDriver: List Network operation failed. Exception: %s" %(str(e)))
+ raise
+
+ network_list = [ network for network in networks['networks'] if network['name'] == mgmt_network ]
+
+ if not network_list:
+ raise NeutronException.NotFound("Could not find network %s" %(mgmt_network))
+ self._mgmt_network_id = network_list[0]['id']
+
+ def validate_account_creds(self):
+ try:
+ ksconn = self.ks_drv._get_keystone_connection()
+ except KeystoneExceptions.AuthorizationFailure as e:
+ logger.error("OpenstackDriver: Unable to authenticate or validate the existing credentials. Exception: %s" %(str(e)))
+ raise ValidationError("Invalid Credentials: "+ str(e))
+ except Exception as e:
+ logger.error("OpenstackDriver: Could not connect to Openstack. Exception: %s" %(str(e)))
+ raise ValidationError("Connection Error: "+ str(e))
+
+ def get_mgmt_network_id(self):
+ return self._mgmt_network_id
+
+ def glance_image_create(self, **kwargs):
+ if not 'disk_format' in kwargs:
+ kwargs['disk_format'] = 'qcow2'
+ if not 'container_format' in kwargs:
+ kwargs['container_format'] = 'bare'
+ if not 'min_disk' in kwargs:
+ kwargs['min_disk'] = 0
+ if not 'min_ram' in kwargs:
+ kwargs['min_ram'] = 0
+ return self.glance_drv.image_create(**kwargs)
+
+ def glance_image_upload(self, image_id, fd):
+ self.glance_drv.image_upload(image_id, fd)
+
+ def glance_image_add_location(self, image_id, location):
+ self.glance_drv.image_add_location(image_id, location)
+
+ def glance_image_delete(self, image_id):
+ self.glance_drv.image_delete(image_id)
+
+ def glance_image_list(self):
+ return self.glance_drv.image_list()
+
+ def glance_image_get(self, image_id):
+ return self.glance_drv.image_get(image_id)
+
+
+ def nova_flavor_list(self):
+ return self.nova_drv.flavor_list()
+
+ def nova_flavor_create(self, name, ram, vcpus, disk, epa_specs):
+ extra_specs = epa_specs if epa_specs else {}
+ return self.nova_drv.flavor_create(name,
+ ram = ram,
+ vcpu = vcpus,
+ disk = disk,
+ extra_specs = extra_specs)
+
+ def nova_flavor_delete(self, flavor_id):
+ self.nova_drv.flavor_delete(flavor_id)
+
+ def nova_flavor_get(self, flavor_id):
+ return self.nova_drv.flavor_get(flavor_id)
+
+ def nova_server_create(self, **kwargs):
+ assert kwargs['flavor_id'] == self.nova_drv.flavor_get(kwargs['flavor_id'])['id']
+ image = self.glance_drv.image_get(kwargs['image_id'])
+ if image['status'] != 'active':
+ raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s" %(image['id'], image['status']))
+
+ # if 'network_list' in kwargs:
+ # kwargs['network_list'].append(self._mgmt_network_id)
+ # else:
+ # kwargs['network_list'] = [self._mgmt_network_id]
+
+ if 'security_groups' not in kwargs:
+ nvconn = self.nova_drv._get_nova_connection()
+ sec_groups = nvconn.security_groups.list()
+ if sec_groups:
+ ## Should we add VM in all availability security_groups ???
+ kwargs['security_groups'] = [x.name for x in sec_groups]
+ else:
+ kwargs['security_groups'] = None
+
+ return self.nova_drv.server_create(**kwargs)
+
+ def nova_server_add_port(self, server_id, port_id):
+ self.nova_drv.server_add_port(server_id, port_id)
+
+ def nova_server_delete_port(self, server_id, port_id):
+ self.nova_drv.server_delete_port(server_id, port_id)
+
+ def nova_server_start(self, server_id):
+ self.nova_drv.server_start(server_id)
+
+ def nova_server_stop(self, server_id):
+ self.nova_drv.server_stop(server_id)
+
+ def nova_server_delete(self, server_id):
+ self.nova_drv.server_delete(server_id)
+
+ def nova_server_reboot(self, server_id):
+ self.nova_drv.server_reboot(server_id, reboot_type='HARD')
+
+ def nova_server_rebuild(self, server_id, image_id):
+ self.nova_drv.server_rebuild(server_id, image_id)
+
+ def nova_floating_ip_list(self):
+ return self.nova_drv.floating_ip_list()
+
+ def nova_floating_ip_create(self, pool = None):
+ return self.nova_drv.floating_ip_create(pool)
+
+ def nova_floating_ip_delete(self, floating_ip):
+ self.nova_drv.floating_ip_delete(floating_ip)
+
+ def nova_floating_ip_assign(self, server_id, floating_ip, fixed_ip):
+ self.nova_drv.floating_ip_assign(server_id, floating_ip, fixed_ip)
+
+ def nova_floating_ip_release(self, server_id, floating_ip):
+ self.nova_drv.floating_ip_release(server_id, floating_ip)
+
+ def nova_server_list(self):
+ return self.nova_drv.server_list()
+
+ def nova_server_get(self, server_id):
+ return self.nova_drv.server_get(server_id)
+
+ def nova_server_console(self, server_id):
+ return self.nova_drv.server_console(server_id)
+
+ def nova_server_group_list(self):
+ return self.nova_drv.group_list()
+
+ def neutron_network_list(self):
+ return self.neutron_drv.network_list()
+
+ def neutron_network_get(self, network_id):
+ return self.neutron_drv.network_get(network_id)
+
+ def neutron_network_create(self, **kwargs):
+ return self.neutron_drv.network_create(**kwargs)
+
+ def neutron_network_delete(self, network_id):
+ self.neutron_drv.network_delete(network_id)
+
+ def neutron_subnet_list(self):
+ return self.neutron_drv.subnet_list()
+
+ def neutron_subnet_get(self, subnet_id):
+ return self.neutron_drv.subnet_get(subnet_id)
+
+ def neutron_subnet_create(self, **kwargs):
+ return self.neutron_drv.subnet_create(**kwargs)
+
+ def netruon_subnet_delete(self, subnet_id):
+ self.neutron_drv.subnet_delete(subnet_id)
+
+ def neutron_subnetpool_list(self):
+ return self.neutron_drv.subnetpool_list()
+
+ def netruon_subnetpool_by_name(self, pool_name):
+ pool_list = self.neutron_drv.subnetpool_list(**{'name': pool_name})
+ if pool_list:
+ return pool_list[0]
+ else:
+ return None
+
+ def neutron_port_list(self, **kwargs):
+ return self.neutron_drv.port_list(**kwargs)
+
+ def neutron_port_get(self, port_id):
+ return self.neutron_drv.port_get(port_id)
+
+ def neutron_port_create(self, **kwargs):
+ subnets = [subnet for subnet in self.neutron_drv.subnet_list() if subnet['network_id'] == kwargs['network_id']]
+ assert len(subnets) == 1
+ kwargs['subnet_id'] = subnets[0]['id']
+ if not 'admin_state_up' in kwargs:
+ kwargs['admin_state_up'] = True
+ port_id = self.neutron_drv.port_create(**kwargs)
+
+ if 'vm_id' in kwargs:
+ self.nova_server_add_port(kwargs['vm_id'], port_id)
+ return port_id
+
+ def neutron_security_group_list(self):
+ return self.neutron_drv.security_group_list()
+
+ def neutron_security_group_by_name(self, group_name):
+ group_list = self.neutron_drv.security_group_list()
+ groups = [group for group in group_list if group['name'] == group_name]
+ if groups:
+ return groups[0]
+ else:
+ return None
+
+ def neutron_port_delete(self, port_id):
+ self.neutron_drv.port_delete(port_id)
+
+ def ceilo_meter_endpoint(self):
+ return self.ceilo_drv.endpoint
+
+ def ceilo_meter_list(self):
+ return self.ceilo_drv.meters
+
+ def ceilo_nfvi_metrics(self, vim_id):
+ """Returns a dict of NFVI metrics for a given VM
+
+ Arguments:
+ vim_id - the VIM ID of the VM to retrieve the metrics for
+
+ Returns:
+ A dict of NFVI metrics
+
+ """
+ def query_latest_sample(counter_name):
+ try:
+ filter = json.dumps({
+ "and": [
+ {"=": {"resource": vim_id}},
+ {"=": {"counter_name": counter_name}}
+ ]
+ })
+ orderby = json.dumps([{"timestamp": "DESC"}])
+ result = self.ceilo_drv.client.query_samples.query(
+ filter=filter,
+ orderby=orderby,
+ limit=1,
+ )
+ return result[0]
+
+ except IndexError:
+ pass
+
+ except Exception as e:
+ logger.error("Got exception while querying ceilometer, exception details:%s " %str(e))
+
+ return None
+
+ memory_usage = query_latest_sample("memory.usage")
+ disk_usage = query_latest_sample("disk.usage")
+ cpu_util = query_latest_sample("cpu_util")
+
+ metrics = dict()
+
+ if memory_usage is not None:
+ memory_usage.volume = 1e6 * memory_usage.volume
+ metrics["memory_usage"] = memory_usage.to_dict()
+
+ if disk_usage is not None:
+ metrics["disk_usage"] = disk_usage.to_dict()
+
+ if cpu_util is not None:
+ metrics["cpu_util"] = cpu_util.to_dict()
+
+ return metrics
+
+ def ceilo_alarm_list(self):
+ """Returns a list of ceilometer alarms"""
+ return self.ceilo_drv.client.alarms.list()
+
+ def ceilo_alarm_create(self,
+ name,
+ meter,
+ statistic,
+ operation,
+ threshold,
+ period,
+ evaluations,
+ severity='low',
+ repeat=True,
+ enabled=True,
+ actions=None,
+ **kwargs):
+ """Create a new Alarm
+
+ Arguments:
+ name - the name of the alarm
+ meter - the name of the meter to measure
+ statistic - the type of statistic used to trigger the alarm
+ ('avg', 'min', 'max', 'count', 'sum')
+ operation - the relational operator that, combined with the
+ threshold value, determines when the alarm is
+ triggered ('lt', 'le', 'eq', 'ge', 'gt')
+ threshold - the value of the statistic that will trigger the
+ alarm
+ period - the duration (seconds) over which to evaluate the
+ specified statistic
+ evaluations - the number of samples of the meter statistic to
+ collect when evaluating the threshold
+ severity - a measure of the urgency or importance of the alarm
+ ('low', 'moderate', 'critical')
+ repeat - a flag that indicates whether the alarm should be
+ triggered once (False) or repeatedly while the alarm
+ condition is true (True)
+ enabled - a flag that indicates whether the alarm is enabled
+ (True) or disabled (False)
+ actions - a dict specifying the URLs for webhooks. The dict can
+ have up to 3 keys: 'insufficient_data', 'alarm',
+ 'ok'. Each key is associated with a list of URLs to
+ webhooks that will be invoked when one of the 3
+ actions is taken.
+ kwargs - an arbitrary dict of keyword arguments that are
+ passed to the ceilometer client
+
+ """
+ ok_actions = actions.get('ok') if actions is not None else None
+ alarm_actions = actions.get('alarm') if actions is not None else None
+ insufficient_data_actions = actions.get('insufficient_data') if actions is not None else None
+
+ return self.ceilo_drv.client.alarms.create(
+ name=name,
+ meter_name=meter,
+ statistic=statistic,
+ comparison_operator=operation,
+ threshold=threshold,
+ period=period,
+ evaluation_periods=evaluations,
+ severity=severity,
+ repeat_actions=repeat,
+ enabled=enabled,
+ ok_actions=ok_actions,
+ alarm_actions=alarm_actions,
+ insufficient_data_actions=insufficient_data_actions,
+ **kwargs
+ )
+
+ def ceilo_alarm_update(self, alarm_id, **kwargs):
+ """Updates an existing alarm
+
+ Arguments:
+ alarm_id - the identifier of the alarm to update
+ kwargs - a dict of the alarm attributes to update
+
+ """
+ return self.ceilo_drv.client.alarms.update(alarm_id, **kwargs)
+
+ def ceilo_alarm_delete(self, alarm_id):
+ self.ceilo_drv.client.alarms.delete(alarm_id)