diff options
author | spisarski <s.pisarski@cablelabs.com> | 2017-10-12 14:17:59 -0600 |
---|---|---|
committer | spisarski <s.pisarski@cablelabs.com> | 2017-10-12 14:17:59 -0600 |
commit | 92d57dd388e5ad292d476298ad79d8a566780e2a (patch) | |
tree | deaa2c6854aa53a6e71cc15c06c9d5d1e57beab2 | |
parent | cef5b452099579a3f69a5c233b7ba25bd0d80f5c (diff) |
Improved creator/state machine classes class hierarchy.
Created abstract superclasses for all classes responsible
for deploying and maintaining the state of objects deployed
to OpenStack which should help developers better understand
the library.
JIRA: SNAPS-183
Change-Id: I7651bd338f0d4e4086abbc11755e6be4f19058bd
Signed-off-by: spisarski <s.pisarski@cablelabs.com>
-rw-r--r-- | examples/launch.py | 13 | ||||
-rw-r--r-- | snaps/domain/creator.py | 44 | ||||
-rw-r--r-- | snaps/openstack/create_flavor.py | 40 | ||||
-rw-r--r-- | snaps/openstack/create_image.py | 53 | ||||
-rw-r--r-- | snaps/openstack/create_instance.py | 111 | ||||
-rw-r--r-- | snaps/openstack/create_keypairs.py | 42 | ||||
-rw-r--r-- | snaps/openstack/create_network.py | 81 | ||||
-rw-r--r-- | snaps/openstack/create_project.py | 65 | ||||
-rw-r--r-- | snaps/openstack/create_router.py | 114 | ||||
-rw-r--r-- | snaps/openstack/create_security_group.py | 85 | ||||
-rw-r--r-- | snaps/openstack/create_stack.py | 56 | ||||
-rw-r--r-- | snaps/openstack/create_user.py | 53 | ||||
-rw-r--r-- | snaps/openstack/openstack_creator.py | 110 | ||||
-rw-r--r-- | snaps/openstack/utils/deploy_utils.py | 70 | ||||
-rw-r--r-- | snaps/openstack/utils/tests/heat_utils_tests.py | 2 |
15 files changed, 603 insertions, 336 deletions
diff --git a/examples/launch.py b/examples/launch.py index f5d3bea..76353a2 100644 --- a/examples/launch.py +++ b/examples/launch.py @@ -164,7 +164,11 @@ def __create_instances(os_creds_dict, creator_class, config_class, config, creator = creator_class( __get_creds(os_creds_dict, os_users_dict, inst_config), config_class(**inst_config)) - creator.create(cleanup=cleanup) + + if cleanup: + creator.initialize() + else: + creator.create() out[inst_config['name']] = creator logger.info('Created configured %s', config_key) except Exception as e: @@ -211,7 +215,7 @@ def __create_vm_instances(os_creds_dict, os_users_dict, instances_config, instance_settings, image_creator.image_settings, keypair_creator=keypairs_dict[kp_name], - cleanup=cleanup) + init_only=cleanup) else: raise Exception('Image creator instance not found.' ' Cannot instantiate') @@ -669,7 +673,6 @@ def main(arguments): logger.error( 'Unexpected error deploying environment. Rolling back due' ' to - ' + str(e)) - # __cleanup(creators) raise # Must enter either block @@ -701,8 +704,8 @@ def main(arguments): def __cleanup(creators, clean_image=False): for creator_dict in reversed(creators): for key, creator in creator_dict.items(): - if (isinstance(creator, OpenStackImage) and clean_image) or \ - not isinstance(creator, OpenStackImage): + if ((isinstance(creator, OpenStackImage) and clean_image) + or not isinstance(creator, OpenStackImage)): try: creator.clean() except Exception as e: diff --git a/snaps/domain/creator.py b/snaps/domain/creator.py new file mode 100644 index 0000000..cbfd030 --- /dev/null +++ b/snaps/domain/creator.py @@ -0,0 +1,44 @@ +# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# 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. + +__author__ = 'spisarski' + + +class CloudObject(object): + """ + Base class for all cloud objects to create or manage + """ + + def initialize(self): + """ + Method used to initialize object via queries + :return: + """ + raise NotImplementedError('Please implement this abstract method') + + def create(self): + """ + Method to be called to create this cloud object. First line should + generally be a call to initialize() + :return: + """ + raise NotImplementedError('Please implement this abstract method') + + def clean(self): + """ + Method used to delete objects on the cloud + :return: + """ + raise NotImplementedError('Please implement this abstract method') diff --git a/snaps/openstack/create_flavor.py b/snaps/openstack/create_flavor.py index ec4c765..1479bb0 100644 --- a/snaps/openstack/create_flavor.py +++ b/snaps/openstack/create_flavor.py @@ -16,6 +16,7 @@ import logging from novaclient.exceptions import NotFound +from snaps.openstack.openstack_creator import OpenStackComputeObject from snaps.openstack.utils import nova_utils __author__ = 'spisarski' @@ -26,7 +27,7 @@ MEM_PAGE_SIZE_ANY = {'hw:mem_page_size': 'any'} MEM_PAGE_SIZE_LARGE = {'hw:mem_page_size': 'large'} -class OpenStackFlavor: +class OpenStackFlavor(OpenStackComputeObject): """ Class responsible for creating a user in OpenStack """ @@ -38,29 +39,36 @@ class OpenStackFlavor: :param flavor_settings: The flavor settings :return: """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.flavor_settings = flavor_settings self.__flavor = None - self.__nova = None - def create(self, cleanup=False): + def initialize(self): """ - Creates the image in OpenStack if it does not already exist - :param cleanup: Denotes whether or not this is being called for cleanup - or not - :return: The OpenStack flavor object + Loads the existing OpenStack flavor + :return: The Flavor domain object or None """ - self.__nova = nova_utils.nova_client(self.__os_creds) + super(self.__class__, self).initialize() + self.__flavor = nova_utils.get_flavor_by_name( - self.__nova, self.flavor_settings.name) + self._nova, self.flavor_settings.name) if self.__flavor: - logger.info( - 'Found flavor with name - ' + self.flavor_settings.name) - elif not cleanup: + logger.info('Found flavor with name - %s', + self.flavor_settings.name) + return self.__flavor + + def create(self): + """ + Creates the image in OpenStack if it does not already exist + :return: The OpenStack flavor object + """ + self.initialize() + if not self.__flavor: self.__flavor = nova_utils.create_flavor( - self.__nova, self.flavor_settings) + self._nova, self.flavor_settings) if self.flavor_settings.metadata: - nova_utils.set_flavor_keys(self.__nova, self.__flavor, + nova_utils.set_flavor_keys(self._nova, self.__flavor, self.flavor_settings.metadata) else: logger.info('Did not create flavor due to cleanup mode') @@ -74,7 +82,7 @@ class OpenStackFlavor: """ if self.__flavor: try: - nova_utils.delete_flavor(self.__nova, self.__flavor) + nova_utils.delete_flavor(self._nova, self.__flavor) except NotFound: pass diff --git a/snaps/openstack/create_image.py b/snaps/openstack/create_image.py index 537824d..3024717 100644 --- a/snaps/openstack/create_image.py +++ b/snaps/openstack/create_image.py @@ -17,6 +17,7 @@ from glanceclient.exc import HTTPNotFound import logging import time +from snaps.openstack.openstack_creator import OpenStackCloudObject from snaps.openstack.utils import glance_utils __author__ = 'spisarski' @@ -28,9 +29,9 @@ POLL_INTERVAL = 3 STATUS_ACTIVE = 'active' -class OpenStackImage: +class OpenStackImage(OpenStackCloudObject): """ - Class responsible for creating an image in OpenStack + Class responsible for managing an image in OpenStack """ def __init__(self, os_creds, image_settings): @@ -40,22 +41,20 @@ class OpenStackImage: :param image_settings: The image settings :return: """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.image_settings = image_settings self.__image = None self.__kernel_image = None self.__ramdisk_image = None self.__glance = None - def create(self, cleanup=False): + def initialize(self): """ - Creates the image in OpenStack if it does not already exist and returns - the domain Image object - :param cleanup: Denotes whether or not this is being called for cleanup - or not - :return: The OpenStack Image object + Loads the existing Image + :return: The Image domain object or None """ - self.__glance = glance_utils.glance_client(self.__os_creds) + self.__glance = glance_utils.glance_client(self._os_creds) self.__image = glance_utils.get_image( self.__glance, image_settings=self.image_settings) if self.__image: @@ -66,15 +65,31 @@ class OpenStackImage: raise ImageCreationError( 'Image with does not exist with name - ' + self.image_settings.name) - elif not cleanup: + + if self.image_settings.kernel_image_settings: + self.__kernel_image = glance_utils.get_image( + self.__glance, + image_settings=self.image_settings.kernel_image_settings) + if self.image_settings.ramdisk_image_settings: + self.__ramdisk_image = glance_utils.get_image( + self.__glance, + image_settings=self.image_settings.ramdisk_image_settings) + + return self.__image + + def create(self): + """ + Creates the image in OpenStack if it does not already exist and returns + the domain Image object + :return: The Image domain object or None + """ + self.initialize() + + if not self.__image: extra_properties = self.image_settings.extra_properties or dict() if self.image_settings.kernel_image_settings: - self.__kernel_image = glance_utils.get_image( - self.__glance, - image_settings=self.image_settings.kernel_image_settings) - - if not self.__kernel_image and not cleanup: + if not self.__kernel_image: logger.info( 'Creating associated kernel image with name - %s', self.image_settings.kernel_image_settings.name) @@ -83,11 +98,7 @@ class OpenStackImage: self.image_settings.kernel_image_settings) extra_properties['kernel_id'] = self.__kernel_image.id if self.image_settings.ramdisk_image_settings: - self.__ramdisk_image = glance_utils.get_image( - self.__glance, - image_settings=self.image_settings.ramdisk_image_settings) - - if not self.__ramdisk_image and not cleanup: + if not self.__ramdisk_image: logger.info( 'Creating associated ramdisk image with name - %s', self.image_settings.ramdisk_image_settings.name) diff --git a/snaps/openstack/create_instance.py b/snaps/openstack/create_instance.py index 2fbeb79..3d55f42 100644 --- a/snaps/openstack/create_instance.py +++ b/snaps/openstack/create_instance.py @@ -19,6 +19,7 @@ from neutronclient.common.exceptions import PortNotFoundClient from novaclient.exceptions import NotFound from snaps.openstack.create_network import PortSettings +from snaps.openstack.openstack_creator import OpenStackComputeObject from snaps.openstack.utils import glance_utils from snaps.openstack.utils import neutron_utils from snaps.openstack.utils import nova_utils @@ -33,9 +34,9 @@ STATUS_ACTIVE = 'ACTIVE' STATUS_DELETED = 'DELETED' -class OpenStackVmInstance: +class OpenStackVmInstance(OpenStackComputeObject): """ - Class responsible for creating a VM instance in OpenStack + Class responsible for managing a VM instance in OpenStack """ def __init__(self, os_creds, instance_settings, image_settings, @@ -48,9 +49,8 @@ class OpenStackVmInstance: :param keypair_settings: The keypair metadata (Optional) :raises Exception """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) - self.__nova = None self.__neutron = None self.instance_settings = instance_settings @@ -65,28 +65,34 @@ class OpenStackVmInstance: # Note: this object does not change after the VM becomes active self.__vm = None - def create(self, cleanup=False, block=False): + def initialize(self): """ - Creates a VM instance - :param cleanup: When true, this object is initialized only via queries, - else objects will be created when the queries return - None. The name of this parameter should be changed to - something like 'readonly' as the same goes with all of - the other creator classes. + Loads the existing VMInst, Port, FloatingIps + :return: VMInst domain object + """ + super(self.__class__, self).initialize() + + self.__neutron = neutron_utils.neutron_client(self._os_creds) + + self.__ports = self.__query_ports(self.instance_settings.port_settings) + self.__lookup_existing_vm_by_name() + + def create(self, block=False): + """ + Creates a VM instance and associated objects unless they already exist :param block: Thread will block until instance has either become active, error, or timeout waiting. Additionally, when True, floating IPs will not be applied until VM is active. - :return: The VM reference object + :return: VMInst domain object """ - self.__nova = nova_utils.nova_client(self.__os_creds) - self.__neutron = neutron_utils.neutron_client(self.__os_creds) + self.initialize() - self.__ports = self.__setup_ports(self.instance_settings.port_settings, - cleanup) - self.__lookup_existing_vm_by_name() - if not self.__vm and not cleanup: + if len(self.__ports) == 0: + self.__ports = self.__create_ports(self.instance_settings.port_settings) + if not self.__vm: self.__create_vm(block) + return self.__vm def __lookup_existing_vm_by_name(self): @@ -96,7 +102,7 @@ class OpenStackVmInstance: within the project """ server = nova_utils.get_server( - self.__nova, vm_inst_settings=self.instance_settings) + self._nova, vm_inst_settings=self.instance_settings) if server: if server.name == self.instance_settings.name: self.__vm = server @@ -124,9 +130,9 @@ class OpenStackVmInstance: active, error, or timeout waiting. Floating IPs will be assigned after active when block=True """ - glance = glance_utils.glance_client(self.__os_creds) + glance = glance_utils.glance_client(self._os_creds) self.__vm = nova_utils.create_server( - self.__nova, self.__neutron, glance, self.instance_settings, + self._nova, self.__neutron, glance, self.instance_settings, self.image_settings, self.keypair_settings) logger.info('Created instance with name - %s', self.instance_settings.name) @@ -140,7 +146,7 @@ class OpenStackVmInstance: # Create server should do this but found it needed to occur here 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, + nova_utils.add_security_group(self._nova, self.__vm, sec_grp_name) else: raise VmInstanceCreationError( @@ -235,7 +241,7 @@ class OpenStackVmInstance: try: logger.info( 'Deleting VM instance - ' + self.instance_settings.name) - nova_utils.delete_vm_instance(self.__nova, self.__vm) + nova_utils.delete_vm_instance(self._nova, self.__vm) except Exception as e: logger.error('Error deleting VM - %s', e) @@ -258,12 +264,11 @@ class OpenStackVmInstance: 'Unexpected error while checking VM instance status - %s', e) - def __setup_ports(self, port_settings, cleanup): + def __query_ports(self, port_settings): """ - Returns the previously configured ports or creates them if they do not + Returns the previously configured ports or an empty list if none exist :param port_settings: A list of PortSetting objects - :param cleanup: When true, only perform lookups for OpenStack objects. :return: a list of OpenStack port tuples where the first member is the port name and the second is the port object """ @@ -272,22 +277,28 @@ class OpenStackVmInstance: for port_setting in port_settings: port = neutron_utils.get_port( self.__neutron, port_settings=port_setting) - if not port: - network = neutron_utils.get_network( - self.__neutron, network_name=port_setting.network_name) - net_ports = neutron_utils.get_ports(self.__neutron, network) - for net_port in net_ports: - if port_setting.mac_address == net_port.mac_address: - port = net_port - break if port: ports.append((port_setting.name, port)) - elif not cleanup: - # Exception will be raised when port with same name already - # exists - ports.append( - (port_setting.name, neutron_utils.create_port( - self.__neutron, self.__os_creds, port_setting))) + + return ports + + def __create_ports(self, port_settings): + """ + Returns the previously configured ports or creates them if they do not + exist + :param port_settings: A list of PortSetting objects + :return: a list of OpenStack port tuples where the first member is the + port name and the second is the port object + """ + ports = list() + + for port_setting in port_settings: + port = neutron_utils.get_port( + self.__neutron, port_settings=port_setting) + if not port: + port = neutron_utils.create_port(self.__neutron, self._os_creds, port_setting) + if port: + ports.append((port_setting.name, port)) return ports @@ -316,7 +327,7 @@ class OpenStackVmInstance: logger.debug('Attempting to add floating IP to instance') try: nova_utils.add_floating_ip_to_server( - self.__nova, self.__vm, floating_ip, ip) + self._nova, self.__vm, floating_ip, ip) logger.info( 'Added floating IP %s to port IP %s on instance %s', floating_ip.ip, ip, self.instance_settings.name) @@ -341,21 +352,21 @@ class OpenStackVmInstance: Returns the OpenStack credentials used to create these objects :return: the credentials """ - return self.__os_creds + return self._os_creds def get_vm_inst(self): """ Returns the latest version of this server object from OpenStack :return: Server object """ - return nova_utils.get_server_object_by_id(self.__nova, self.__vm.id) + return nova_utils.get_server_object_by_id(self._nova, self.__vm.id) def get_console_output(self): """ Returns the vm console object for parsing logs :return: the console output object """ - return nova_utils.get_server_console_output(self.__nova, self.__vm) + return nova_utils.get_server_console_output(self._nova, self.__vm) def get_port_ip(self, port_name, subnet_name=None): """ @@ -415,7 +426,7 @@ class OpenStackVmInstance: Returns a dictionary of a VMs info as returned by OpenStack :return: a dict() """ - return nova_utils.get_server_info(self.__nova, self.__vm) + return nova_utils.get_server_info(self._nova, self.__vm) def config_nics(self): """ @@ -490,7 +501,7 @@ class OpenStackVmInstance: return ansible_utils.apply_playbook( pb_file_loc, [self.get_floating_ip(fip_name=fip_name).ip], self.get_image_user(), self.keypair_settings.private_filepath, - variables, self.__os_creds.proxy_settings) + variables, self._os_creds.proxy_settings) def get_image_user(self): """ @@ -582,7 +593,7 @@ class OpenStackVmInstance: else: return False - status = nova_utils.get_server_status(self.__nova, self.__vm) + status = nova_utils.get_server_status(self._nova, self.__vm) if not status: logger.warning('Cannot find instance with id - ' + self.__vm.id) return False @@ -667,7 +678,7 @@ class OpenStackVmInstance: self.__get_first_provisioning_floating_ip().ip, self.get_image_user(), self.keypair_settings.private_filepath, - proxy_settings=self.__os_creds.proxy_settings) + proxy_settings=self._os_creds.proxy_settings) else: logger.warning( 'Cannot return an SSH client. No Floating IP configured') @@ -685,7 +696,7 @@ class OpenStackVmInstance: return False try: - nova_utils.add_security_group(self.__nova, self.get_vm_inst(), + nova_utils.add_security_group(self._nova, self.get_vm_inst(), security_group.name) return True except NotFound as e: @@ -705,7 +716,7 @@ class OpenStackVmInstance: return False try: - nova_utils.remove_security_group(self.__nova, self.get_vm_inst(), + nova_utils.remove_security_group(self._nova, self.get_vm_inst(), security_group) return True except NotFound as e: diff --git a/snaps/openstack/create_keypairs.py b/snaps/openstack/create_keypairs.py index d0b69cd..3869afc 100644 --- a/snaps/openstack/create_keypairs.py +++ b/snaps/openstack/create_keypairs.py @@ -19,6 +19,7 @@ from neutronclient.common.utils import str2bool from novaclient.exceptions import NotFound from snaps import file_utils +from snaps.openstack.openstack_creator import OpenStackComputeObject from snaps.openstack.utils import nova_utils __author__ = 'spisarski' @@ -26,9 +27,9 @@ __author__ = 'spisarski' logger = logging.getLogger('OpenStackKeypair') -class OpenStackKeypair: +class OpenStackKeypair(OpenStackComputeObject): """ - Class responsible for creating a keypair in OpenStack + Class responsible for managing a keypair in OpenStack """ def __init__(self, os_creds, keypair_settings): @@ -37,38 +38,43 @@ class OpenStackKeypair: :param os_creds: The credentials to connect with OpenStack :param keypair_settings: The settings used to create a keypair """ - self.__nova = None - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.keypair_settings = keypair_settings - self.__nova = nova_utils.nova_client(os_creds) self.__delete_keys_on_clean = True # Attributes instantiated on create() self.__keypair = None - def create(self, cleanup=False): + def initialize(self): """ - Responsible for creating the keypair object. - :param cleanup: Denotes whether or not this is being called for cleanup - or not + Loads the existing OpenStack Keypair + :return: The Keypair domain object or None """ - self.__nova = nova_utils.nova_client(self.__os_creds) - - logger.info('Creating keypair %s...' % self.keypair_settings.name) + super(self.__class__, self).initialize() try: self.__keypair = nova_utils.get_keypair_by_name( - self.__nova, self.keypair_settings.name) + self._nova, self.keypair_settings.name) + return self.__keypair except Exception as e: logger.warn('Cannot load existing keypair - %s', e) - return - if not self.__keypair and not cleanup: + def create(self): + """ + Responsible for creating the keypair object. + :return: The Keypair domain object or None + """ + self.initialize() + + if not self.__keypair: + logger.info('Creating keypair %s...' % self.keypair_settings.name) + if self.keypair_settings.public_filepath and os.path.isfile( self.keypair_settings.public_filepath): logger.info("Uploading existing keypair") self.__keypair = nova_utils.upload_keypair_file( - self.__nova, self.keypair_settings.name, + self._nova, self.keypair_settings.name, self.keypair_settings.public_filepath) if self.keypair_settings.delete_on_clean is not None: @@ -80,7 +86,7 @@ class OpenStackKeypair: logger.info("Creating new keypair") keys = nova_utils.create_keys(self.keypair_settings.key_size) self.__keypair = nova_utils.upload_keypair( - self.__nova, self.keypair_settings.name, + self._nova, self.keypair_settings.name, nova_utils.public_key_openssh(keys)) file_utils.save_keys_to_files( keys, self.keypair_settings.public_filepath, @@ -104,7 +110,7 @@ class OpenStackKeypair: """ if self.__keypair: try: - nova_utils.delete_keypair(self.__nova, self.__keypair) + nova_utils.delete_keypair(self._nova, self.__keypair) except NotFound: pass self.__keypair = None diff --git a/snaps/openstack/create_network.py b/snaps/openstack/create_network.py index 166a682..437f294 100644 --- a/snaps/openstack/create_network.py +++ b/snaps/openstack/create_network.py @@ -15,6 +15,8 @@ import logging from neutronclient.common.exceptions import NotFound + +from snaps.openstack.openstack_creator import OpenStackNetworkObject from snaps.openstack.utils import keystone_utils, neutron_utils __author__ = 'spisarski' @@ -22,9 +24,9 @@ __author__ = 'spisarski' logger = logging.getLogger('OpenStackNetwork') -class OpenStackNetwork: +class OpenStackNetwork(OpenStackNetworkObject): """ - Class responsible for creating a network in OpenStack + Class responsible for managing a network in OpenStack """ def __init__(self, os_creds, network_settings): @@ -33,56 +35,61 @@ class OpenStackNetwork: :param os_creds: The credentials to connect with OpenStack :param network_settings: The settings used to create a network """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.network_settings = network_settings - self.__neutron = None # Attributes instantiated on create() self.__network = None self.__subnets = list() - def create(self, cleanup=False): + def initialize(self): + """ + Loads the existing OpenStack network/subnet + :return: The Network domain object or None + """ + super(self.__class__, self).initialize() + + self.__network = neutron_utils.get_network( + self._neutron, network_settings=self.network_settings, + project_id=self.network_settings.get_project_id(self._os_creds)) + + if self.__network: + for subnet_setting in self.network_settings.subnet_settings: + sub_inst = neutron_utils.get_subnet( + self._neutron, subnet_settings=subnet_setting) + if sub_inst: + self.__subnets.append(sub_inst) + logger.debug( + "Subnet '%s' created successfully" % sub_inst.id) + + return self.__network + + def create(self): """ Responsible for creating not only the network but then a private subnet, router, and an interface to the router. - :param cleanup: When true, only perform lookups for OpenStack objects. - :return: the created network object or None + :return: the Network domain object """ - self.__neutron = neutron_utils.neutron_client(self.__os_creds) - - logger.info( - 'Creating neutron network %s...' % self.network_settings.name) - net_inst = neutron_utils.get_network( - self.__neutron, network_settings=self.network_settings, - project_id=self.network_settings.get_project_id(self.__os_creds)) - if net_inst: - self.__network = net_inst - else: - if not cleanup: - self.__network = neutron_utils.create_network( - self.__neutron, self.__os_creds, self.network_settings) - else: - logger.info( - 'Network does not exist and will not create as in cleanup' - ' mode') - return - logger.debug( - "Network '%s' created successfully" % self.__network.id) + self.initialize() + + if not self.__network: + self.__network = neutron_utils.create_network( + self._neutron, self._os_creds, self.network_settings) + logger.debug( + "Network '%s' created successfully" % self.__network.id) - logger.debug('Creating Subnets....') for subnet_setting in self.network_settings.subnet_settings: sub_inst = neutron_utils.get_subnet( - self.__neutron, subnet_settings=subnet_setting) + self._neutron, subnet_settings=subnet_setting) + if not sub_inst: + sub_inst = neutron_utils.create_subnet( + self._neutron, subnet_setting, self._os_creds, + self.__network) if sub_inst: self.__subnets.append(sub_inst) logger.debug( "Subnet '%s' created successfully" % sub_inst.id) - else: - if not cleanup: - self.__subnets.append( - neutron_utils.create_subnet( - self.__neutron, subnet_setting, self.__os_creds, - self.__network)) return self.__network @@ -94,7 +101,7 @@ class OpenStackNetwork: try: logger.info( 'Deleting subnet with name ' + subnet.name) - neutron_utils.delete_subnet(self.__neutron, subnet) + neutron_utils.delete_subnet(self._neutron, subnet) except NotFound as e: logger.warning( 'Error deleting subnet with message - ' + str(e)) @@ -103,7 +110,7 @@ class OpenStackNetwork: if self.__network: try: - neutron_utils.delete_network(self.__neutron, self.__network) + neutron_utils.delete_network(self._neutron, self.__network) except NotFound: pass diff --git a/snaps/openstack/create_project.py b/snaps/openstack/create_project.py index 38505ad..0cf6d4a 100644 --- a/snaps/openstack/create_project.py +++ b/snaps/openstack/create_project.py @@ -16,6 +16,7 @@ import logging from keystoneclient.exceptions import NotFound, Conflict +from snaps.openstack.openstack_creator import OpenStackIdentityObject from snaps.openstack.utils import keystone_utils, neutron_utils, nova_utils __author__ = 'spisarski' @@ -23,9 +24,9 @@ __author__ = 'spisarski' logger = logging.getLogger('create_image') -class OpenStackProject: +class OpenStackProject(OpenStackIdentityObject): """ - Class responsible for creating a project/project in OpenStack + Class responsible for managing a project/project in OpenStack """ def __init__(self, os_creds, project_settings): @@ -35,38 +36,42 @@ class OpenStackProject: :param project_settings: The project's settings :return: """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.project_settings = project_settings self.__project = None self.__role = None - self.__keystone = None self.__role_name = self.project_settings.name + '-role' - def create(self, cleanup=False): + def initialize(self): """ - Creates the image in OpenStack if it does not already exist - :param cleanup: Denotes whether or not this is being called for cleanup - :return: The OpenStack Image object + Loads the existing Project object if it exists + :return: The Project domain object """ - self.__keystone = keystone_utils.keystone_client(self.__os_creds) + super(self.__class__, self).initialize() + self.__project = keystone_utils.get_project( - keystone=self.__keystone, project_settings=self.project_settings) - if self.__project: - logger.info( - 'Found project with name - ' + self.project_settings.name) - elif not cleanup: + keystone=self._keystone, project_settings=self.project_settings) + return self.__project + + def create(self): + """ + Creates a Project/Tenant in OpenStack if it does not already exist + :return: The Project domain object + """ + self.initialize() + + if not self.__project: self.__project = keystone_utils.create_project( - self.__keystone, self.project_settings) + self._keystone, self.project_settings) for username in self.project_settings.users: - user = keystone_utils.get_user(self.__keystone, username) + user = keystone_utils.get_user(self._keystone, username) if user: try: self.assoc_user(user) except Conflict as e: logger.warn('Unable to associate user %s due to %s', user.name, e) - else: - logger.info('Did not create image due to cleanup mode') return self.__project @@ -77,7 +82,7 @@ class OpenStackProject: """ if self.__project: # Delete security group 'default' if exists - neutron = neutron_utils.neutron_client(self.__os_creds) + neutron = neutron_utils.neutron_client(self._os_creds) default_sec_grp = neutron_utils.get_security_group( neutron, sec_grp_name='default', project_id=self.__project.id) @@ -90,23 +95,23 @@ class OpenStackProject: # Delete Project try: - keystone_utils.delete_project(self.__keystone, self.__project) + keystone_utils.delete_project(self._keystone, self.__project) except NotFound: pass self.__project = None if self.__role: try: - keystone_utils.delete_role(self.__keystone, self.__role) + keystone_utils.delete_role(self._keystone, self.__role) except NotFound: pass self.__project = None # Final role check in case init was done from an existing instance role = keystone_utils.get_role_by_name( - self.__keystone, self.__role_name) + self._keystone, self.__role_name) if role: - keystone_utils.delete_role(self.__keystone, role) + keystone_utils.delete_role(self._keystone, role) def get_project(self): """ @@ -123,12 +128,12 @@ class OpenStackProject: """ if not self.__role: self.__role = keystone_utils.get_role_by_name( - self.__keystone, self.__role_name) + self._keystone, self.__role_name) if not self.__role: self.__role = keystone_utils.create_role( - self.__keystone, self.__role_name) + self._keystone, self.__role_name) - keystone_utils.grant_user_role_to_project(self.__keystone, self.__role, + keystone_utils.grant_user_role_to_project(self._keystone, self.__role, user, self.__project) def get_compute_quotas(self): @@ -136,7 +141,7 @@ class OpenStackProject: Returns the compute quotas as an instance of the ComputeQuotas class :return: """ - nova = nova_utils.nova_client(self.__os_creds) + nova = nova_utils.nova_client(self._os_creds) return nova_utils.get_compute_quotas(nova, self.__project.id) def get_network_quotas(self): @@ -144,7 +149,7 @@ class OpenStackProject: Returns the network quotas as an instance of the NetworkQuotas class :return: """ - neutron = neutron_utils.neutron_client(self.__os_creds) + neutron = neutron_utils.neutron_client(self._os_creds) return neutron_utils.get_network_quotas(neutron, self.__project.id) def update_compute_quotas(self, compute_quotas): @@ -152,7 +157,7 @@ class OpenStackProject: Updates the compute quotas for this project :param compute_quotas: a ComputeQuotas object. """ - nova = nova_utils.nova_client(self.__os_creds) + nova = nova_utils.nova_client(self._os_creds) nova_utils.update_quotas(nova, self.__project.id, compute_quotas) def update_network_quotas(self, network_quotas): @@ -160,7 +165,7 @@ class OpenStackProject: Updates the network quotas for this project :param network_quotas: a NetworkQuotas object. """ - neutron = neutron_utils.neutron_client(self.__os_creds) + neutron = neutron_utils.neutron_client(self._os_creds) neutron_utils.update_quotas(neutron, self.__project.id, network_quotas) diff --git a/snaps/openstack/create_router.py b/snaps/openstack/create_router.py index ef27fab..98e3e14 100644 --- a/snaps/openstack/create_router.py +++ b/snaps/openstack/create_router.py @@ -16,6 +16,7 @@ import logging from neutronclient.common.exceptions import NotFound from snaps.openstack.create_network import PortSettings +from snaps.openstack.openstack_creator import OpenStackNetworkObject from snaps.openstack.utils import neutron_utils, keystone_utils __author__ = 'spisarski' @@ -23,9 +24,9 @@ __author__ = 'spisarski' logger = logging.getLogger('OpenStackNetwork') -class OpenStackRouter: +class OpenStackRouter(OpenStackNetworkObject): """ - Class responsible for creating a router in OpenStack + Class responsible for managing a router in OpenStack """ def __init__(self, os_creds, router_settings): @@ -36,13 +37,12 @@ class OpenStackRouter: (must be an instance of the RouterSettings class) """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) if not router_settings: raise RouterCreationError('router_settings is required') self.router_settings = router_settings - self.__neutron = None # Attributes instantiated on create() self.__router = None @@ -53,64 +53,84 @@ class OpenStackRouter: # interfaces are the value self.__ports = list() - def create(self, cleanup=False): + def initialize(self): """ - Responsible for creating the router. - :param cleanup: When true, only perform lookups for OpenStack objects. - :return: the router object + Loads the existing router. + :return: the Router domain object """ - self.__neutron = neutron_utils.neutron_client(self.__os_creds) - - logger.debug( - 'Creating Router with name - ' + self.router_settings.name) - existing = False - router_inst = neutron_utils.get_router( - self.__neutron, router_settings=self.router_settings) - if router_inst: - self.__router = router_inst - existing = True - else: - if not cleanup: - self.__router = neutron_utils.create_router( - self.__neutron, self.__os_creds, self.router_settings) + super(self.__class__, self).initialize() + + self.__router = neutron_utils.get_router( + self._neutron, router_settings=self.router_settings) for internal_subnet_name in self.router_settings.internal_subnets: internal_subnet = neutron_utils.get_subnet( - self.__neutron, subnet_name=internal_subnet_name) + self._neutron, subnet_name=internal_subnet_name) if internal_subnet: self.__internal_subnets.append(internal_subnet) - if internal_subnet and not cleanup and not existing: - logger.debug('Adding router to subnet...') - router_intf = neutron_utils.add_interface_router( - self.__neutron, self.__router, subnet=internal_subnet) - self.__internal_router_interface = router_intf else: raise RouterCreationError( 'Subnet not found with name ' + internal_subnet_name) for port_setting in self.router_settings.port_settings: port = neutron_utils.get_port( - self.__neutron, port_settings=port_setting) - logger.info( - 'Retrieved port %s for router - %s', port_setting.name, - self.router_settings.name) + self._neutron, port_settings=port_setting) if port: self.__ports.append(port) - if not port and not cleanup and not existing: - port = neutron_utils.create_port(self.__neutron, - self.__os_creds, port_setting) - if port: - logger.info( - 'Created port %s for router - %s', port_setting.name, - self.router_settings.name) - self.__ports.append(port) - neutron_utils.add_interface_router(self.__neutron, - self.__router, - port=port) + return self.__router + + def create(self): + """ + Responsible for creating the router. + :return: the Router domain object + """ + self.initialize() + + if not self.__router: + self.__router = neutron_utils.create_router( + self._neutron, self._os_creds, self.router_settings) + + for internal_subnet_name in self.router_settings.internal_subnets: + internal_subnet = neutron_utils.get_subnet( + self._neutron, subnet_name=internal_subnet_name) + if internal_subnet: + self.__internal_subnets.append(internal_subnet) + if internal_subnet: + logger.debug('Adding router to subnet...') + router_intf = neutron_utils.add_interface_router( + self._neutron, self.__router, + subnet=internal_subnet) + self.__internal_router_interface = router_intf else: raise RouterCreationError( - 'Error creating port with name - ' + port_setting.name) + 'Subnet not found with name ' + internal_subnet_name) + + for port_setting in self.router_settings.port_settings: + port = neutron_utils.get_port( + self._neutron, port_settings=port_setting) + logger.info( + 'Retrieved port %s for router - %s', port_setting.name, + self.router_settings.name) + if port: + self.__ports.append(port) + + if not port: + port = neutron_utils.create_port( + self._neutron, self._os_creds, port_setting) + if port: + logger.info( + 'Created port %s for router - %s', + port_setting.name, + self.router_settings.name) + self.__ports.append(port) + neutron_utils.add_interface_router(self._neutron, + self.__router, + port=port) + else: + raise RouterCreationError( + 'Error creating port with name - ' + + port_setting.name) return self.__router @@ -123,7 +143,7 @@ class OpenStackRouter: 'Removing router interface from router %s and port %s', self.router_settings.name, port.name) try: - neutron_utils.remove_interface_router(self.__neutron, + neutron_utils.remove_interface_router(self._neutron, self.__router, port=port) except NotFound: pass @@ -134,7 +154,7 @@ class OpenStackRouter: 'Removing router interface from router %s and subnet %s', self.router_settings.name, internal_subnet.name) try: - neutron_utils.remove_interface_router(self.__neutron, + neutron_utils.remove_interface_router(self._neutron, self.__router, subnet=internal_subnet) except NotFound: @@ -144,7 +164,7 @@ class OpenStackRouter: if self.__router: logger.info('Removing router ' + self.router_settings.name) try: - neutron_utils.delete_router(self.__neutron, self.__router) + neutron_utils.delete_router(self._neutron, self.__router) except NotFound: pass self.__router = None diff --git a/snaps/openstack/create_security_group.py b/snaps/openstack/create_security_group.py index 34d5952..8218c83 100644 --- a/snaps/openstack/create_security_group.py +++ b/snaps/openstack/create_security_group.py @@ -16,6 +16,8 @@ import logging import enum from neutronclient.common.exceptions import NotFound, Conflict + +from snaps.openstack.openstack_creator import OpenStackNetworkObject from snaps.openstack.utils import keystone_utils from snaps.openstack.utils import neutron_utils @@ -24,9 +26,9 @@ __author__ = 'spisarski' logger = logging.getLogger('OpenStackSecurityGroup') -class OpenStackSecurityGroup: +class OpenStackSecurityGroup(OpenStackNetworkObject): """ - Class responsible for creating Security Groups + Class responsible for managing a Security Group in OpenStack """ def __init__(self, os_creds, sec_grp_settings): @@ -35,10 +37,9 @@ class OpenStackSecurityGroup: :param os_creds: The credentials to connect with OpenStack :param sec_grp_settings: The settings used to create a security group """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.sec_grp_settings = sec_grp_settings - self.__neutron = None - self.__keystone = None # Attributes instantiated on create() self.__security_group = None @@ -46,29 +47,46 @@ class OpenStackSecurityGroup: # dict where the rule settings object is the key self.__rules = dict() - def create(self, cleanup=False): + def initialize(self): + """ + Loads existing security group. + :return: the security group domain object + """ + super(self.__class__, self).initialize() + + self.__security_group = neutron_utils.get_security_group( + self._neutron, sec_grp_settings=self.sec_grp_settings) + if self.__security_group: + # Populate rules + existing_rules = neutron_utils.get_rules_by_security_group( + self._neutron, self.__security_group) + + for existing_rule in existing_rules: + # For Custom Rules + rule_setting = self.__get_setting_from_rule(existing_rule) + self.__rules[rule_setting] = existing_rule + + return self.__security_group + + def create(self): """ Responsible for creating the security group. - :param cleanup: Denotes whether or not this is being called for cleanup - :return: the OpenStack security group object + :return: the security group domain object """ - self.__neutron = neutron_utils.neutron_client(self.__os_creds) - self.__keystone = keystone_utils.keystone_client(self.__os_creds) + self.initialize() - logger.info( - 'Creating security group %s...' % self.sec_grp_settings.name) + if not self.__security_group: + logger.info( + 'Creating security group %s...' % self.sec_grp_settings.name) - self.__security_group = neutron_utils.get_security_group( - self.__neutron, sec_grp_settings=self.sec_grp_settings) - if not self.__security_group and not cleanup: - # Create the security group + keystone = keystone_utils.keystone_client(self._os_creds) self.__security_group = neutron_utils.create_security_group( - self.__neutron, self.__keystone, + self._neutron, keystone, self.sec_grp_settings) # Get the rules added for free auto_rules = neutron_utils.get_rules_by_security_group( - self.__neutron, self.__security_group) + self._neutron, self.__security_group) ctr = 0 for auto_rule in auto_rules: @@ -80,7 +98,7 @@ class OpenStackSecurityGroup: for sec_grp_rule_setting in self.sec_grp_settings.rule_settings: try: custom_rule = neutron_utils.create_security_group_rule( - self.__neutron, sec_grp_rule_setting) + self._neutron, sec_grp_rule_setting) self.__rules[sec_grp_rule_setting] = custom_rule except Conflict as e: logger.warn('Unable to create rule due to conflict - %s', @@ -88,22 +106,7 @@ class OpenStackSecurityGroup: # Refresh security group object to reflect the new rules added self.__security_group = neutron_utils.get_security_group( - self.__neutron, sec_grp_settings=self.sec_grp_settings) - else: - # Populate rules - existing_rules = neutron_utils.get_rules_by_security_group( - self.__neutron, self.__security_group) - - for existing_rule in existing_rules: - # For Custom Rules - rule_setting = self.__get_setting_from_rule(existing_rule) - ctr = 0 - if not rule_setting: - # For Free Rules - rule_setting = self.__generate_rule_setting(existing_rule) - ctr += 1 - - self.__rules[rule_setting] = existing_rule + self._neutron, sec_grp_settings=self.sec_grp_settings) return self.__security_group @@ -115,7 +118,7 @@ class OpenStackSecurityGroup: :return: the newly instantiated SecurityGroupRuleSettings object """ sec_grp = neutron_utils.get_security_group_by_id( - self.__neutron, rule.security_group_id) + self._neutron, rule.security_group_id) setting = SecurityGroupRuleSettings( description=rule.description, @@ -135,7 +138,7 @@ class OpenStackSecurityGroup: """ for setting, rule in self.__rules.items(): try: - neutron_utils.delete_security_group_rule(self.__neutron, rule) + neutron_utils.delete_security_group_rule(self._neutron, rule) except NotFound as e: logger.warning('Rule not found, cannot delete - ' + str(e)) pass @@ -143,7 +146,7 @@ class OpenStackSecurityGroup: if self.__security_group: try: - neutron_utils.delete_security_group(self.__neutron, + neutron_utils.delete_security_group(self._neutron, self.__security_group) except NotFound as e: logger.warning( @@ -171,7 +174,7 @@ class OpenStackSecurityGroup: :param rule_setting: the rule configuration """ rule_setting.sec_grp_name = self.sec_grp_settings.name - new_rule = neutron_utils.create_security_group_rule(self.__neutron, + new_rule = neutron_utils.create_security_group_rule(self._neutron, rule_setting) self.__rules[rule_setting] = new_rule self.sec_grp_settings.rule_settings.append(rule_setting) @@ -187,12 +190,12 @@ class OpenStackSecurityGroup: if rule_id or rule_setting: if rule_id: rule_to_remove = neutron_utils.get_rule_by_id( - self.__neutron, self.__security_group, rule_id) + self._neutron, self.__security_group, rule_id) elif rule_setting: rule_to_remove = self.__rules.get(rule_setting) if rule_to_remove: - neutron_utils.delete_security_group_rule(self.__neutron, + neutron_utils.delete_security_group_rule(self._neutron, rule_to_remove) rule_setting = self.__get_setting_from_rule(rule_to_remove) if rule_setting: diff --git a/snaps/openstack/create_stack.py b/snaps/openstack/create_stack.py index ffe87a5..ce87e89 100644 --- a/snaps/openstack/create_stack.py +++ b/snaps/openstack/create_stack.py @@ -19,6 +19,7 @@ import time from heatclient.exc import HTTPNotFound from snaps.openstack.create_instance import OpenStackVmInstance +from snaps.openstack.openstack_creator import OpenStackCloudObject from snaps.openstack.utils import nova_utils, settings_utils, glance_utils from snaps.openstack.create_network import OpenStackNetwork @@ -36,9 +37,9 @@ STATUS_DELETE_COMPLETE = 'DELETE_COMPLETE' STATUS_DELETE_FAILED = 'DELETE_FAILED' -class OpenStackHeatStack: +class OpenStackHeatStack(OpenStackCloudObject, object): """ - Class responsible for creating an heat stack in OpenStack + Class responsible for managing a heat stack in OpenStack """ def __init__(self, os_creds, stack_settings, image_settings=None, @@ -55,7 +56,8 @@ class OpenStackHeatStack: used for spawning this stack :return: """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.stack_settings = stack_settings if image_settings: @@ -71,24 +73,30 @@ class OpenStackHeatStack: self.__stack = None self.__heat_cli = None - def create(self, cleanup=False): + def initialize(self): """ - Creates the heat stack in OpenStack if it does not already exist and - returns the domain Stack object - :param cleanup: When true, this object is initialized only via queries, - else objects will be created when the queries return - None. The name of this parameter should be changed to - something like 'readonly' as the same goes with all of - the other creator classes. - :return: The OpenStack Stack object + Loads the existing heat stack + :return: The Stack domain object or None """ - self.__heat_cli = heat_utils.heat_client(self.__os_creds) + self.__heat_cli = heat_utils.heat_client(self._os_creds) self.__stack = heat_utils.get_stack( self.__heat_cli, stack_settings=self.stack_settings) if self.__stack: logger.info('Found stack with name - ' + self.stack_settings.name) return self.__stack - elif not cleanup: + + def create(self): + """ + Creates the heat stack in OpenStack if it does not already exist and + returns the domain Stack object + :return: The Stack domain object or None + """ + self.initialize() + + if self.__stack: + logger.info('Found stack with name - ' + self.stack_settings.name) + return self.__stack + else: self.__stack = heat_utils.create_stack(self.__heat_cli, self.stack_settings) logger.info( @@ -102,10 +110,6 @@ class OpenStackHeatStack: raise StackCreationError( 'Stack was not created or activated in the alloted amount ' 'of time') - else: - logger.info('Did not create stack due to cleanup mode') - - return self.__stack def clean(self): """ @@ -211,7 +215,7 @@ class OpenStackHeatStack: :return: list() of OpenStackNetwork objects """ - neutron = neutron_utils.neutron_client(self.__os_creds) + neutron = neutron_utils.neutron_client(self._os_creds) out = list() stack_networks = heat_utils.get_stack_networks( @@ -220,9 +224,9 @@ class OpenStackHeatStack: for stack_network in stack_networks: net_settings = settings_utils.create_network_settings( neutron, stack_network) - net_creator = OpenStackNetwork(self.__os_creds, net_settings) + net_creator = OpenStackNetwork(self._os_creds, net_settings) out.append(net_creator) - net_creator.create(cleanup=True) + net_creator.initialize() return out @@ -234,13 +238,13 @@ class OpenStackHeatStack: """ out = list() - nova = nova_utils.nova_client(self.__os_creds) + nova = nova_utils.nova_client(self._os_creds) stack_servers = heat_utils.get_stack_servers( self.__heat_cli, nova, self.__stack) - neutron = neutron_utils.neutron_client(self.__os_creds) - glance = glance_utils.glance_client(self.__os_creds) + neutron = neutron_utils.neutron_client(self._os_creds) + glance = glance_utils.glance_client(self._os_creds) for stack_server in stack_servers: vm_inst_settings = settings_utils.create_vm_inst_settings( @@ -252,10 +256,10 @@ class OpenStackHeatStack: keypair_settings=self.keypair_settings, priv_key_key=heat_keypair_option) vm_inst_creator = OpenStackVmInstance( - self.__os_creds, vm_inst_settings, image_settings, + self._os_creds, vm_inst_settings, image_settings, keypair_settings) out.append(vm_inst_creator) - vm_inst_creator.create(cleanup=True) + vm_inst_creator.initialize() return out diff --git a/snaps/openstack/create_user.py b/snaps/openstack/create_user.py index 6db74fe..bcf4790 100644 --- a/snaps/openstack/create_user.py +++ b/snaps/openstack/create_user.py @@ -15,6 +15,8 @@ import logging from keystoneclient.exceptions import NotFound + +from snaps.openstack.openstack_creator import OpenStackIdentityObject from snaps.openstack.os_credentials import OSCreds from snaps.openstack.utils import keystone_utils @@ -23,9 +25,9 @@ __author__ = 'spisarski' logger = logging.getLogger('create_user') -class OpenStackUser: +class OpenStackUser(OpenStackIdentityObject): """ - Class responsible for creating a user in OpenStack + Class responsible for managing a user in OpenStack """ def __init__(self, os_creds, user_settings): @@ -35,24 +37,31 @@ class OpenStackUser: :param user_settings: The user settings :return: """ - self.__os_creds = os_creds + super(self.__class__, self).__init__(os_creds) + self.user_settings = user_settings self.__user = None - self.__keystone = None - def create(self, cleanup=False): + def initialize(self): """ Creates the user in OpenStack if it does not already exist - :param cleanup: Denotes whether or not this is being called for cleanup - :return: The OpenStack user object + :return: The User domain object """ - self.__keystone = keystone_utils.keystone_client(self.__os_creds) - self.__user = keystone_utils.get_user(self.__keystone, + super(self.__class__, self).initialize() + + self.__user = keystone_utils.get_user(self._keystone, self.user_settings.name) - if not self.__user and not cleanup: - self.__user = keystone_utils.create_user(self.__keystone, - self.user_settings) + return self.__user + def create(self, cleanup=False): + """ + Creates a User if one does not already exist + :return: The User domain object + """ + self.initialize() + if not self.__user: + self.__user = keystone_utils.create_user(self._keystone, + self.user_settings) return self.__user def clean(self): @@ -62,7 +71,7 @@ class OpenStackUser: """ if self.__user: try: - keystone_utils.delete_user(self.__keystone, self.__user) + keystone_utils.delete_user(self._keystone, self.__user) except NotFound: pass self.__user = None @@ -84,16 +93,16 @@ class OpenStackUser: return OSCreds( username=self.user_settings.name, password=self.user_settings.password, - auth_url=self.__os_creds.auth_url, + auth_url=self._os_creds.auth_url, project_name=project_name, - identity_api_version=self.__os_creds.identity_api_version, - user_domain_name=self.__os_creds.user_domain_name, - user_domain_id=self.__os_creds.user_domain_id, - project_domain_name=self.__os_creds.project_domain_name, - project_domain_id=self.__os_creds.project_domain_id, - interface=self.__os_creds.interface, - proxy_settings=self.__os_creds.proxy_settings, - cacert=self.__os_creds.cacert) + identity_api_version=self._os_creds.identity_api_version, + user_domain_name=self._os_creds.user_domain_name, + user_domain_id=self._os_creds.user_domain_id, + project_domain_name=self._os_creds.project_domain_name, + project_domain_id=self._os_creds.project_domain_id, + interface=self._os_creds.interface, + proxy_settings=self._os_creds.proxy_settings, + cacert=self._os_creds.cacert) class UserSettings: diff --git a/snaps/openstack/openstack_creator.py b/snaps/openstack/openstack_creator.py new file mode 100644 index 0000000..de2ae91 --- /dev/null +++ b/snaps/openstack/openstack_creator.py @@ -0,0 +1,110 @@ +# Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs") +# and others. All rights reserved. +# +# 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. +from snaps.domain.creator import CloudObject +from snaps.openstack.utils import nova_utils, neutron_utils, keystone_utils + +__author__ = 'spisarski' + + +class OpenStackCloudObject(CloudObject): + """ + Abstract class for all OpenStack object creators + """ + + def __init__(self, os_creds): + """ + Constructor + :param os_creds: the OpenStack credentials object + """ + # super(self.__class__, self, os_creds) + self._os_creds = os_creds + + def initialize(self): + raise NotImplementedError('Do not override abstract method') + + def create(self): + raise NotImplementedError('Do not override abstract method') + + def clean(self): + raise NotImplementedError('Do not override abstract method') + + +class OpenStackComputeObject(OpenStackCloudObject): + """ + Abstract class for all OpenStack compute creators + """ + + def __init__(self, os_creds): + """ + Constructor + :param os_creds: the OpenStack credentials object + """ + super(OpenStackComputeObject, self).__init__(os_creds) + self._nova = None + + def initialize(self): + self._nova = nova_utils.nova_client(self._os_creds) + + def create(self): + raise NotImplementedError('Do not override abstract method') + + def clean(self): + raise NotImplementedError('Do not override abstract method') + + +class OpenStackNetworkObject(OpenStackCloudObject): + """ + Abstract class for all OpenStack compute creators + """ + + def __init__(self, os_creds): + """ + Constructor + :param os_creds: the OpenStack credentials object + """ + super(OpenStackNetworkObject, self).__init__(os_creds) + self._neutron = None + + def initialize(self): + self._neutron = neutron_utils.neutron_client(self._os_creds) + + def create(self): + raise NotImplementedError('Do not override abstract method') + + def clean(self): + raise NotImplementedError('Do not override abstract method') + + +class OpenStackIdentityObject(OpenStackCloudObject): + """ + Abstract class for all OpenStack compute creators + """ + + def __init__(self, os_creds): + """ + Constructor + :param os_creds: the OpenStack credentials object + """ + super(OpenStackIdentityObject, self).__init__(os_creds) + self._keystone = None + + def initialize(self): + self._keystone = keystone_utils.keystone_client(self._os_creds) + + def create(self): + raise NotImplementedError('Do not override abstract method') + + def clean(self): + raise NotImplementedError('Do not override abstract method') diff --git a/snaps/openstack/utils/deploy_utils.py b/snaps/openstack/utils/deploy_utils.py index ade8811..c936c1f 100644 --- a/snaps/openstack/utils/deploy_utils.py +++ b/snaps/openstack/utils/deploy_utils.py @@ -34,11 +34,16 @@ def create_image(os_creds, image_settings, cleanup=False): Creates an image in OpenStack if necessary :param os_creds: The OpenStack credentials object :param image_settings: The image settings object - :param cleanup: Denotes whether or not this is being called for cleanup or not - :return: A reference to the image creator object from which the image object can be accessed + :param cleanup: Denotes whether or not this is being called for cleanup + :return: A reference to the image creator object from which the image + object can be accessed """ image_creator = OpenStackImage(os_creds, image_settings) - image_creator.create(cleanup) + + if cleanup: + image_creator.initialize() + else: + image_creator.create() return image_creator @@ -47,18 +52,24 @@ def create_network(os_creds, network_settings, cleanup=False): Creates a network on which the CMTSs can attach :param os_creds: The OpenStack credentials object :param network_settings: The network settings object - :param cleanup: Denotes whether or not this is being called for cleanup or not - :return: A reference to the network creator objects for each network from which network elements such as the - subnet, router, interface router, and network objects can be accessed. + :param cleanup: Denotes whether or not this is being called for cleanup + :return: A reference to the network creator objects for each network from + which network elements such as the subnet, router, interface + router, and network objects can be accessed. """ # Check for OS for network existence # If exists return network instance data # Else, create network and return instance data - logger.info('Attempting to create network with name - ' + network_settings.name) + logger.info('Attempting to create network with name - %s', + network_settings.name) network_creator = OpenStackNetwork(os_creds, network_settings) - network_creator.create(cleanup) + + if cleanup: + network_creator.initialize() + else: + network_creator.create() logger.info('Created network ') return network_creator @@ -68,16 +79,22 @@ def create_router(os_creds, router_settings, cleanup=False): Creates a network on which the CMTSs can attach :param os_creds: The OpenStack credentials object :param router_settings: The RouterSettings instance - :param cleanup: Denotes whether or not this is being called for cleanup or not - :return: A reference to the network creator objects for each network from which network elements such as the - subnet, router, interface router, and network objects can be accessed. + :param cleanup: Denotes whether or not this is being called for cleanup + :return: A reference to the network creator objects for each network from + which network elements such as the subnet, router, interface + router, and network objects can be accessed. """ # Check for OS for network existence # If exists return network instance data # Else, create network and return instance data - logger.info('Attempting to create router with name - ' + router_settings.name) + logger.info('Attempting to create router with name - %s', + router_settings.name) router_creator = OpenStackRouter(os_creds, router_settings) - router_creator.create(cleanup) + + if cleanup: + router_creator.initialize() + else: + router_creator.create() logger.info('Created router ') return router_creator @@ -87,30 +104,40 @@ def create_keypair(os_creds, keypair_settings, cleanup=False): Creates a keypair that can be applied to an instance :param os_creds: The OpenStack credentials object :param keypair_settings: The KeypairSettings object - :param cleanup: Denotes whether or not this is being called for cleanup or not + :param cleanup: Denotes whether or not this is being called for cleanup :return: A reference to the keypair creator object """ keypair_creator = OpenStackKeypair(os_creds, keypair_settings) - keypair_creator.create(cleanup) + + if cleanup: + keypair_creator.initialize() + else: + keypair_creator.create() return keypair_creator -def create_vm_instance(os_creds, instance_settings, image_settings, keypair_creator=None, cleanup=False): +def create_vm_instance(os_creds, instance_settings, image_settings, + keypair_creator=None, init_only=False): """ Creates a VM instance :param os_creds: The OpenStack credentials :param instance_settings: Instance of VmInstanceSettings :param image_settings: The object containing image settings - :param keypair_creator: The object responsible for creating the keypair associated with this VM instance. (optional) - :param sg_names: The names of the security groups to apply to VM. (optional) - :param cleanup: Denotes whether or not this is being called for cleanup or not (default False) + :param keypair_creator: The object responsible for creating the keypair + associated with this VM instance. (optional) + :param init_only: Denotes whether or not this is being called for + initialization (T) or creation (F) (default False) :return: A reference to the VM instance object """ kp_settings = None if keypair_creator: kp_settings = keypair_creator.keypair_settings - vm_creator = OpenStackVmInstance(os_creds, instance_settings, image_settings, kp_settings) - vm_creator.create(cleanup=cleanup) + vm_creator = OpenStackVmInstance(os_creds, instance_settings, + image_settings, kp_settings) + if init_only: + vm_creator.initialize() + else: + vm_creator.create() return vm_creator @@ -148,4 +175,3 @@ def create_security_group(os_creds, sec_grp_settings): sg_creator = OpenStackSecurityGroup(os_creds, sec_grp_settings) sg_creator.create() return sg_creator - diff --git a/snaps/openstack/utils/tests/heat_utils_tests.py b/snaps/openstack/utils/tests/heat_utils_tests.py index e7d6265..44235ff 100644 --- a/snaps/openstack/utils/tests/heat_utils_tests.py +++ b/snaps/openstack/utils/tests/heat_utils_tests.py @@ -371,7 +371,7 @@ class HeatUtilsCreateComplexStackTests(OSComponentTestCase): self.image_creator2.image_settings]) vm_creator = OpenStackVmInstance( self.os_creds, vm_settings, img_settings) - vm_creator.create(cleanup=False) + vm_creator.initialize() vm_creator.clean() vm_creator.vm_deleted(block=True) |