Ensure creators' constructors cannot raise exceptions.
[snaps.git] / snaps / openstack / create_instance.py
index caddc05..e1e38a1 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
 #                    and others.  All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,8 +49,8 @@ class OpenStackVmInstance:
         """
         self.__os_creds = os_creds
 
-        self.__nova = nova_utils.nova_client(self.__os_creds)
-        self.__neutron = neutron_utils.neutron_client(self.__os_creds)
+        self.__nova = None
+        self.__neutron = None
 
         self.instance_settings = instance_settings
         self.image_settings = image_settings
@@ -74,16 +74,14 @@ class OpenStackVmInstance:
                       Additionally, when True, floating IPs will not be applied until VM is active.
         :return: The VM reference object
         """
-        try:
-            self.__ports = self.__setup_ports(self.instance_settings.port_settings, cleanup)
-            self.__lookup_existing_vm_by_name()
-            if not self.__vm and not cleanup:
-                self.__create_vm(block)
-            return self.__vm
-        except Exception as e:
-            logger.exception('Error occurred while setting up instance')
-            self.clean()
-            raise e
+        self.__nova = nova_utils.nova_client(self.__os_creds)
+        self.__neutron = neutron_utils.neutron_client(self.__os_creds)
+
+        self.__ports = self.__setup_ports(self.instance_settings.port_settings, cleanup)
+        self.__lookup_existing_vm_by_name()
+        if not self.__vm and not cleanup:
+            self.__create_vm(block)
+        return self.__vm
 
     def __lookup_existing_vm_by_name(self):
         """
@@ -122,7 +120,7 @@ class OpenStackVmInstance:
         if not flavor:
             raise Exception('Flavor not found with name - ' + self.instance_settings.flavor)
 
-        image = glance_utils.get_image(self.__nova, glance_utils.glance_client(self.__os_creds),
+        image = glance_utils.get_image(glance_utils.glance_client(self.__os_creds),
                                        self.image_settings.name)
         if image:
             self.__vm = self.__nova.servers.create(
@@ -131,16 +129,27 @@ class OpenStackVmInstance:
                 image=image,
                 nics=nics,
                 key_name=keypair_name,
-                security_groups=list(self.instance_settings.security_group_names),
+                security_groups=self.instance_settings.security_group_names,
                 userdata=self.instance_settings.userdata,
                 availability_zone=self.instance_settings.availability_zone)
+
         else:
             raise Exception('Cannot create instance, image cannot be located with name ' + self.image_settings.name)
 
         logger.info('Created instance with name - ' + self.instance_settings.name)
 
         if block:
-            self.vm_active(block=True)
+            if not self.vm_active(block=True):
+                raise Exception('Fatal error, VM did not become ACTIVE within the alloted time')
+
+        # TODO - the call above should add security groups. The return object shows they exist but the association
+        # had never been made by OpenStack. This call is here to ensure they have been added
+        for sec_grp_name in self.instance_settings.security_group_names:
+            if self.vm_active(block=True):
+                nova_utils.add_security_group(self.__nova, self.__vm, sec_grp_name)
+            else:
+                raise Exception('Cannot applying security group with name ' + sec_grp_name +
+                                ' to VM that did not activate with name - ' + self.instance_settings.name)
 
         self.__apply_floating_ips()
 
@@ -199,7 +208,7 @@ class OpenStackVmInstance:
                 logger.info('Deleting Floating IP - ' + floating_ip.ip)
                 nova_utils.delete_floating_ip(self.__nova, floating_ip)
             except Exception as e:
-                logger.error('Error deleting Floating IP - ' + e.message)
+                logger.error('Error deleting Floating IP - ' + str(e))
         self.__floating_ips = list()
         self.__floating_ip_dict = dict()
 
@@ -209,7 +218,7 @@ class OpenStackVmInstance:
             try:
                 neutron_utils.delete_port(self.__neutron, port)
             except PortNotFoundClient as e:
-                logger.warn('Unexpected error deleting port - ' + e.message)
+                logger.warning('Unexpected error deleting port - ' + str(e))
                 pass
         self.__ports = list()
 
@@ -232,7 +241,7 @@ class OpenStackVmInstance:
                     logger.error('VM not deleted within the timeout period of ' +
                                  str(self.instance_settings.vm_delete_timeout) + ' seconds')
             except Exception as e:
-                logger.error('Unexpected error while checking VM instance status - ' + e.message)
+                logger.error('Unexpected error while checking VM instance status - ' + str(e))
 
     def __setup_ports(self, port_settings, cleanup):
         """
@@ -292,7 +301,7 @@ class OpenStackVmInstance:
                                 ' on instance - ' + self.instance_settings.name)
                     return
                 except Exception as e:
-                    logger.debug('Retry adding floating IP to instance. Last attempt failed with - ' + e.message)
+                    logger.debug('Retry adding floating IP to instance. Last attempt failed with - ' + str(e))
                     time.sleep(poll_interval)
                     count -= 1
                     pass
@@ -330,7 +339,7 @@ class OpenStackVmInstance:
             if subnet_name:
                 subnet = neutron_utils.get_subnet_by_name(self.__neutron, subnet_name)
                 if not subnet:
-                    logger.warn('Cannot retrieve port IP as subnet could not be located with name - ' + subnet_name)
+                    logger.warning('Cannot retrieve port IP as subnet could not be located with name - ' + subnet_name)
                     return None
                 for fixed_ip in port_dict['fixed_ips']:
                     if fixed_ip['subnet_id'] == subnet['subnet']['id']:
@@ -363,7 +372,7 @@ class OpenStackVmInstance:
         for key, port in self.__ports:
             if key == port_name:
                 return port
-        logger.warn('Cannot find port with name - ' + port_name)
+        logger.warning('Cannot find port with name - ' + port_name)
         return None
 
     def config_nics(self):
@@ -416,7 +425,7 @@ class OpenStackVmInstance:
                                          [floating_ip], self.get_image_user(), self.keypair_settings.private_filepath,
                                          variables, self.__os_creds.proxy_settings)
         else:
-            logger.warn('VM ' + self.instance_settings.name + ' cannot self configure NICs eth1++. ' +
+            logger.warning('VM ' + self.instance_settings.name + ' cannot self configure NICs eth1++. ' +
                         'No playbook  or keypairs found.')
 
     def get_image_user(self):
@@ -441,7 +450,7 @@ class OpenStackVmInstance:
             return self.__vm_status_check(STATUS_DELETED, block, self.instance_settings.vm_delete_timeout,
                                           poll_interval)
         except NotFound as e:
-            logger.debug("Instance not found when querying status for " + STATUS_DELETED + ' with message ' + e.message)
+            logger.debug("Instance not found when querying status for " + STATUS_DELETED + ' with message ' + str(e))
             return True
 
     def vm_active(self, block=False, poll_interval=POLL_INTERVAL):
@@ -489,7 +498,7 @@ class OpenStackVmInstance:
         """
         instance = self.__nova.servers.get(self.__vm.id)
         if not instance:
-            logger.warn('Cannot find instance with id - ' + self.__vm.id)
+            logger.warning('Cannot find instance with id - ' + self.__vm.id)
             return False
 
         if instance.status == 'ERROR':
@@ -564,7 +573,7 @@ class OpenStackVmInstance:
                                             self.keypair_settings.private_filepath,
                                             proxy_settings=self.__os_creds.proxy_settings)
         else:
-            logger.warn('Cannot return an SSH client. No Floating IP configured')
+            logger.warning('Cannot return an SSH client. No Floating IP configured')
 
     def add_security_group(self, security_group):
         """
@@ -575,14 +584,14 @@ class OpenStackVmInstance:
         self.vm_active(block=True)
 
         if not security_group:
-            logger.warn('Security group object is None, cannot add')
+            logger.warning('Security group object is None, cannot add')
             return False
 
         try:
             nova_utils.add_security_group(self.__nova, self.get_vm_inst(), security_group['security_group']['name'])
             return True
         except NotFound as e:
-            logger.warn('Security group not added - ' + e.message)
+            logger.warning('Security group not added - ' + str(e))
             return False
 
     def remove_security_group(self, security_group):
@@ -594,14 +603,14 @@ class OpenStackVmInstance:
         self.vm_active(block=True)
 
         if not security_group:
-            logger.warn('Security group object is None, cannot add')
+            logger.warning('Security group object is None, cannot remove')
             return False
 
         try:
-            nova_utils.remove_security_group(self.__nova, self.get_vm_inst(), security_group['security_group']['name'])
+            nova_utils.remove_security_group(self.__nova, self.get_vm_inst(), security_group)
             return True
         except NotFound as e:
-            logger.warn('Security group not added - ' + e.message)
+            logger.warning('Security group not removed - ' + str(e))
             return False
 
 
@@ -618,7 +627,7 @@ class VmInstanceSettings:
                        member's the key and overrides any of the other parameters.
         :param name: the name of the VM
         :param flavor: the VM's flavor
-        :param port_settings: the port configuration settings
+        :param port_settings: the port configuration settings (required)
         :param security_group_names: a set of names of the security groups to add to the VM
         :param floating_ip_settings: the floating IP configuration settings
         :param sudo_user: the sudo user of the VM that will override the instance_settings.image_user when trying to
@@ -648,7 +657,7 @@ class VmInstanceSettings:
                     self.security_group_names = set(config['security_group_names'])
                 elif isinstance(config['security_group_names'], set):
                     self.security_group_names = config['security_group_names']
-                elif isinstance(config['security_group_names'], basestring):
+                elif isinstance(config['security_group_names'], str):
                     self.security_group_names = [config['security_group_names']]
                 else:
                     raise Exception('Invalid data type for security_group_names attribute')
@@ -698,6 +707,9 @@ class VmInstanceSettings:
         if not self.name or not self.flavor:
             raise Exception('Instance configuration requires the attributes: name, flavor')
 
+        if len(self.port_settings) == 0:
+            raise Exception('Instance configuration requires port settings (aka. NICS)')
+
 
 class FloatingIpSettings:
     """