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:
                                 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')
                 logger.error(
                     'Unexpected error deploying environment. Rolling back due'
                     ' to - ' + str(e))
-                # __cleanup(creators)
                 raise
 
         # Must enter either block
 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:
 
--- /dev/null
+# 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')
 
 
 from novaclient.exceptions import NotFound
 
+from snaps.openstack.openstack_creator import OpenStackComputeObject
 from snaps.openstack.utils import nova_utils
 
 __author__ = 'spisarski'
 MEM_PAGE_SIZE_LARGE = {'hw:mem_page_size': 'large'}
 
 
-class OpenStackFlavor:
+class OpenStackFlavor(OpenStackComputeObject):
     """
     Class responsible for creating a user in OpenStack
     """
         :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')
         """
         if self.__flavor:
             try:
-                nova_utils.delete_flavor(self.__nova, self.__flavor)
+                nova_utils.delete_flavor(self._nova, self.__flavor)
             except NotFound:
                 pass
 
 
 import logging
 import time
 
+from snaps.openstack.openstack_creator import OpenStackCloudObject
 from snaps.openstack.utils import glance_utils
 
 __author__ = 'spisarski'
 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):
         :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:
             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)
                         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)
 
 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
 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,
         :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
         # 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):
         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
                       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)
         # 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(
             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)
 
                     '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
         """
         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
 
                 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)
         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):
         """
         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):
         """
         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):
         """
             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
                 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')
             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:
             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:
 
 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'
 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):
         :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:
                 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,
         """
         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
 
 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'
 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):
         :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
 
             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))
 
         if self.__network:
             try:
-                neutron_utils.delete_network(self.__neutron, self.__network)
+                neutron_utils.delete_network(self._neutron, self.__network)
             except NotFound:
                 pass
 
 
 
 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'
 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):
         :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
 
         """
         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)
 
             # 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):
         """
         """
         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):
         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):
         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):
         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):
         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)
 
 
 
 
 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'
 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):
                                 (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
         # 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
 
                 '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
                 '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:
         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
 
 
 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
 
 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):
         :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
         # 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:
             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',
 
             # 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
 
         :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,
         """
         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
 
         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(
         :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)
         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:
 
 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
 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,
                                  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:
         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(
                 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):
         """
         :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(
         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
 
         """
 
         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(
                 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
 
 
 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
 
 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):
         :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):
         """
         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
         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:
 
--- /dev/null
+# 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')
 
     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
 
 
     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
 
     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
 
     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
 
 
     sg_creator = OpenStackSecurityGroup(os_creds, sec_grp_settings)
     sg_creator.create()
     return sg_creator
-
 
                              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)