def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info('create_base_image() called for Nova plugin - creating a BaseImage') self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update(5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = {'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': False} builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = NIB(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update(10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') os_image_id = self.nib.wait_for_completion(180) if os_image_id: builder.base_image.properties[PROPERTY_NAME_GLANCE_ID] = os_image_id builder.base_image.update(100, 'COMPLETE', 'Image stored in glance with id (%s)' % os_image_id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg)
def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info( 'create_base_image() called for Nova plugin - creating a BaseImage' ) self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update( 5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = { 'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': parameters.get('direct_boot', False), 'timeout': parameters.get('timeout', 1800), 'public': parameters.get('public', False), 'floating_ip': parameters.get('request_floating_ip', False) } builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = Builder(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update( 10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') jeos_image_id = self.nib.wait_for_completion(180) if jeos_image_id: builder.base_image.update( 30, 'BUILDING', 'JEOS image in glance with id (%s), starting customization...' % jeos_image_id) self.log.debug( 'Launching Nova instance with image (%s) for customization & icicle generation.' % jeos_image_id) img_name = self.nib.env.glance.images.get(jeos_image_id).name jeos_instance = self.nib.env.launch_instance( name='Customize %s and Generate ICICLE' % img_name, root_disk=jeos_image_id, flavor=parameters.get('flavor', None)) if not jeos_instance: raise ImageFactoryException( 'Reached timeout waiting for customization instance...') self.log.debug( 'Launched Nova instance (id: %s) for customization & icicle generation.' % jeos_instance.id) if not jeos_instance.open_ssh( ): # Add a security group for ssh access raise ImageFactoryException( 'Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user') private_key_file = jeos_instance.key_dir + jeos_instance.key_pair.name # Get an IP address to use below for ssh connections to the instance. if self._networking_is_active_for_instance(jeos_instance): if install_config['floating_ip']: jeos_instance_addr = self._create_ipaddr_for_instance( jeos_instance) else: jeos_instance_addr = self._get_ipaddr_for_instance( jeos_instance, user, private_key_file) if not jeos_instance_addr: jeos_instance_addr = self._create_ipaddr_for_instance( jeos_instance) else: raise ImageFactoryException( 'Networking not active for instance %s, cannot continue!' % jeos_instance.id) if not jeos_instance_addr: raise ImageFactoryException( 'Unable to obtain an IP address for instance %s' % jeos_instance.id) # Enable root for customization steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix( user, cmd_prefix, jeos_instance_addr, private_key_file): self.log.debug( 'Temporarily enabled root user for image customization steps...' ) else: raise ImageFactoryException( 'Unable to access %s as root. Cannot continue...' % jeos_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException( 'No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=TDL(str(template)), config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, jeos_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(jeos_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.base_image.update( 90, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % jeos_image_id) builder.base_image.icicle = oz_guest.do_icicle( jeos_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(jeos_instance_addr, private_key_file, user, cmd_prefix) self.log.debug( 'Disabling root ssh access now that customization is complete...' ) jeos_instance.close_ssh( ) # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % jeos_instance_addr) if jeos_instance.shutoff(): base_image_id = jeos_instance.create_snapshot(template.name + '-base') builder.base_image.properties[ PROPERTY_NAME_GLANCE_ID] = base_image_id builder.base_image.update( 100, 'COMPLETE', 'Image stored in glance with id (%s)' % base_image_id) jeos_instance.terminate() else: raise ImageFactoryException( 'JEOS build instance (%s) never shutdown in Nova.' % jeos_instance.id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg)
class Nova(object): """ Nova implements the ImageFactory OSDelegate interface for the Nova plugin. """ def __init__(self): super(Nova, self).__init__() self.app_config = ApplicationConfiguration().configuration self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) self.nib = None self._cloud_plugin_content = [] def abort(self): """ Abort the current operation. """ if self.nib and isinstance(self.nib, Builder): status = self.nib.abort() self.log.debug('aborting... status: %s' % status) else: self.log.debug( 'No active Nova Image Builder instance found, nothing to abort.' ) def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info( 'create_base_image() called for Nova plugin - creating a BaseImage' ) self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update( 5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = { 'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': parameters.get('direct_boot', False), 'timeout': parameters.get('timeout', 1800), 'public': parameters.get('public', False), 'floating_ip': parameters.get('request_floating_ip', False) } builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = Builder(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update( 10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') jeos_image_id = self.nib.wait_for_completion(180) if jeos_image_id: builder.base_image.update( 30, 'BUILDING', 'JEOS image in glance with id (%s), starting customization...' % jeos_image_id) self.log.debug( 'Launching Nova instance with image (%s) for customization & icicle generation.' % jeos_image_id) img_name = self.nib.env.glance.images.get(jeos_image_id).name jeos_instance = self.nib.env.launch_instance( name='Customize %s and Generate ICICLE' % img_name, root_disk=jeos_image_id, flavor=parameters.get('flavor', None)) if not jeos_instance: raise ImageFactoryException( 'Reached timeout waiting for customization instance...') self.log.debug( 'Launched Nova instance (id: %s) for customization & icicle generation.' % jeos_instance.id) if not jeos_instance.open_ssh( ): # Add a security group for ssh access raise ImageFactoryException( 'Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user') private_key_file = jeos_instance.key_dir + jeos_instance.key_pair.name # Get an IP address to use below for ssh connections to the instance. if self._networking_is_active_for_instance(jeos_instance): if install_config['floating_ip']: jeos_instance_addr = self._create_ipaddr_for_instance( jeos_instance) else: jeos_instance_addr = self._get_ipaddr_for_instance( jeos_instance, user, private_key_file) if not jeos_instance_addr: jeos_instance_addr = self._create_ipaddr_for_instance( jeos_instance) else: raise ImageFactoryException( 'Networking not active for instance %s, cannot continue!' % jeos_instance.id) if not jeos_instance_addr: raise ImageFactoryException( 'Unable to obtain an IP address for instance %s' % jeos_instance.id) # Enable root for customization steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix( user, cmd_prefix, jeos_instance_addr, private_key_file): self.log.debug( 'Temporarily enabled root user for image customization steps...' ) else: raise ImageFactoryException( 'Unable to access %s as root. Cannot continue...' % jeos_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException( 'No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=TDL(str(template)), config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, jeos_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(jeos_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.base_image.update( 90, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % jeos_image_id) builder.base_image.icicle = oz_guest.do_icicle( jeos_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(jeos_instance_addr, private_key_file, user, cmd_prefix) self.log.debug( 'Disabling root ssh access now that customization is complete...' ) jeos_instance.close_ssh( ) # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % jeos_instance_addr) if jeos_instance.shutoff(): base_image_id = jeos_instance.create_snapshot(template.name + '-base') builder.base_image.properties[ PROPERTY_NAME_GLANCE_ID] = base_image_id builder.base_image.update( 100, 'COMPLETE', 'Image stored in glance with id (%s)' % base_image_id) jeos_instance.terminate() else: raise ImageFactoryException( 'JEOS build instance (%s) never shutdown in Nova.' % jeos_instance.id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg) def create_target_image(self, builder, target, base_image, parameters): """ *** NOT YET IMPLEMENTED *** Performs cloud specific customization on the base image. @param builder The builder object. @param base_image The BaseImage to customize. @param target The cloud type to customize for. @param parameters Dictionary of target specific parameters. @return A TargetImage object. """ self.log.info( 'create_target_image() called for Nova plugin - creating a TargetImage' ) # Merge together any TDL-style customizations requested via our plugin-to-plugin interface with any target # specific packages, repos and commands and then run a second Oz customization step. tdl = TDL(xmlstring=builder.target_image.template, rootpw_required=self.app_config['tdl_require_root_pw']) # We remove any packages, commands and files from the original TDL - these have already been # installed/executed. We leave the repos in place, as it is possible that the target # specific packages or commands may require them. tdl.packages = [] tdl.commands = {} tdl.files = {} # Get user defined repositories and packages from a local config file repositories, packages = self._target_content(tdl, target) if repositories: tdl.merge_repositories(repositories) if packages: tdl.merge_packages(packages) # Content provided by the target plugin for the target plugin if len(self._cloud_plugin_content) > 0: tdl = self.merge_cloud_content_with_tdl(self._cloud_plugin_content, tdl) # If there are no new commands, packages or files, we can stop here if (len(tdl.packages) + len(tdl.commands) + len(tdl.files)) == 0: self.log.debug( 'No further modification of the TargetImage to perform in the OS Plugin - returning' ) return base_image_id = base_image.properties[PROPERTY_NAME_GLANCE_ID] stack_env = StackEnvironment() base_instance = stack_env.launch_instance( name='Target Image Prep', root_disk=('glance', base_image_id), flavor=parameters.get('flavor')) if not base_instance: raise ImageFactoryException( 'Reached timeout waiting for base instance...') self.log.debug( 'Launched Nova instance (id: %s) for target specific customization.' % base_instance.id) if not base_instance.open_ssh(): # Add A security group for ssh access raise ImageFactoryException( 'Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user', 'root') private_key_file = base_instance.key_dir + base_instance.key_pair.name # Get an IP address to use for ssh connections if self._networking_is_active_for_instance(base_instance): if parameters.get('request_floating_ip', False): base_instance_addr = self._create_ipaddr_for_instance( base_instance) else: base_instance_addr = self._get_ipaddr_for_instance( base_instance, user, private_key_file) if not base_instance_addr: base_instance_addr = self._create_ipaddr_for_instance( base_instance) else: raise ImageFactoryException( 'Networking not active for instance %s, cannot continue!' % base_instance.id) if not base_instance_addr: raise ImageFactoryException( 'Unable to obtain IP address for instance %s' % base_instance.id) # Enable root for target prep steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix( user, cmd_prefix, base_instance_addr, private_key_file): self.log.debug( 'Temporarily enabled root user for target preparation steps...' ) else: raise ImageFactoryException( 'Unable to access %s as root. Cannot continue...' % base_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException( 'No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=tdl, config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, base_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(base_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.target_image.update( 85, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % base_image_id) builder.target_image.icicle = oz_guest.do_icicle( base_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(base_instance_addr, private_key_file, user, cmd_prefix) self.log.debug( 'Disabling root ssh access now that customization is complete...' ) base_instance.close_ssh() # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % base_instance_addr) if base_instance.shutoff(): target_image_id = base_instance.create_snapshot(tdl.name + '-base') builder.target_image.properties[ PROPERTY_NAME_GLANCE_ID] = target_image_id builder.target_image.update( 90, 'BUILDING', 'Target Image stored in glance with id (%s)' % target_image_id) base_instance.terminate() else: raise ImageFactoryException( 'JEOS build instance (%s) never shutdown in Nova.' % base_instance.id) builder.target_image.update(95, 'BUILDING', 'Downloading target image...') target_img_download = StackEnvironment().download_image_from_glance( target_image_id) with open(builder.target_image.data, 'wb') as target_img_file: shutil.copyfileobj(target_img_download, target_img_file) target_img_file.close() target_img_download.close() def add_cloud_plugin_content(self, content): """ This is a method that cloud plugins can call to deposit content/commands to be run during the OS-specific first stage of the Target Image creation. There is no support for repos at the moment as these introduce external dependencies that we may not be able to resolve. @param content dict containing commands and file. """ self._cloud_plugin_content.append(content) def merge_cloud_content_with_tdl(self, contents, tdl): """ Merge 'files' and 'commands' content into an existing TDL instance. @param contents: Array of content. @param tdl: TDL instance @return: @raise ImageFactoryException: """ for item in contents: if 'files' in item: for entry in item['files']: if not 'name' in entry: raise ImageFactoryException( 'File given without a name') if not 'type' in entry: raise ImageFactoryException( 'File given without a type') if not 'file' in entry: raise ImageFactoryException( 'File given without any content') if entry['type'] == 'raw': tdl.files[entry['name']] = entry['file'] elif entry['type'] == 'base64': if len(entry['file']) == 0: self.log.warning( 'File given with zero length... %s' % entry['name']) tdl.files[entry['name']] = '' else: tdl.files[entry['name']] = b64decode(entry['file']) else: raise ImageFactoryException( 'File given with invalid type (%s)' % entry['type']) if 'commands' in item: for entry in item['commands']: if not 'name' in entry: raise ImageFactoryException( 'Command given without a name') if not 'type' in entry: raise ImageFactoryException( 'Command given without a type') if not 'command' in entry: raise ImageFactoryException( 'Command given without any content') if entry['type'] == 'raw': tdl.commands[entry['name']] = entry['command'] elif entry['type'] == 'base64': if len(entry['command']) == 0: self.log.warning( 'Command given with zero length... %s' % entry['name']) tdl.commands[entry['name']] = '' else: tdl.commands[entry['name']] = b64decode( entry['command']) else: raise ImageFactoryException( 'Command given with invalid type (%s)' % entry['type']) return tdl def _target_content(self, tdl, target): target_xml = '/etc/imagefactory/target_content.xml' if os.path.isfile(target_xml): doc = libxml2.parseFile(target_xml) else: self.log.debug( "Found neither a call-time config nor a config file - doing nothing" ) return None, None # We go from most to least specific in this order: # arch -> version -> os-> target # Note that at the moment we even allow an include statment that covers absolutely everything. # That is, one that doesn't even specify a target - this is to support a very simple call-time syntax include = doc.xpathEval( "/template_includes/include[@target='%s' and @os='%s' and @version='%s' and @arch='%s']" % (target, tdl.distro, tdl.update, tdl.arch)) if len(include) == 0: include = doc.xpathEval( "/template_includes/include[@target='%s' and @os='%s' and @version='%s' and \ not(@arch)]" % (target, tdl.distro, tdl.update)) if len(include) == 0: include = doc.xpathEval( "/template_includes/include[@target='%s' and @os='%s' and not(@version) and \ not(@arch)]" % (target, tdl.distro)) if len(include) == 0: include = doc.xpathEval( "/template_includes/include[@target='%s' and not(@os) and not(@version) and \ not(@arch)]" % target) if len(include) == 0: include = doc.xpathEval( "/template_includes/include[not(@target) and not(@os) and not(@version) and \ not(@arch)]") if len(include) == 0: self.log.debug( "cannot find a config section that matches our build details - doing nothing" ) return None, None # OK - We have at least one config block that matches our build - take the first one, merge it and be done # TODO: Merge all of them? Err out if there is more than one? Warn? include = include[0] packages = include.xpathEval("packages") if len(packages) > 0: ret_pkgs = packages[0] else: ret_pkgs = None repositories = include.xpathEval("repositories") if len(repositories) > 0: ret_repos = repositories[0] else: ret_repos = None return ret_repos, ret_pkgs def _confirm_ssh_access(self, guest, addr, timeout=300): confirmation = False for index in range(timeout / 10): if index % 10 == 0: self.log.debug('Checking ssh access to %s - %d' % (addr, index)) try: guest.guest_execute_command(addr, '/bin/true', timeout=10) confirmation = True break except Exception as e: self.log.exception( 'Caught exception while checking ssh access to %s: %s' % (addr, e)) sleep(1) if not confirmation: self.log.debug( 'Unable to confirm ssh access to %s after %s minutes...' % (addr, timeout / 60)) return confirmation def _oz_config(self, private_key_file): config = SafeConfigParser() if config.read("/etc/oz/oz.cfg"): config.set('paths', 'output_dir', self.app_config['imgdir']) config.set('paths', 'sshprivkey', private_key_file) if 'oz_data_dir' in self.app_config: config.set('paths', 'data_dir', self.app_config['oz_data_dir']) if 'oz_screenshot_dir' in self.app_config: config.set('paths', 'screenshot_dir', self.app_config['oz_screenshot_dir']) return config else: return None def _networking_is_active_for_instance(self, srvr_instance): for index in range(0, 120, 5): self.log.debug('Polling for networks associated with instance %s' % srvr_instance.id) if len(srvr_instance.instance.networks) > 0: return True else: sleep(5) return False def _get_ipaddr_for_instance(self, srvr_instance, user, key): for index in range(0, 300, 5): try: for network in srvr_instance.instance.networks.values(): for address in network: try: stdout, stderr, retcode = ssh_execute_command( str(address), key, '/bin/id', user=user) if retcode == 0: self.log.debug('Connected to %s' % address) return address except Exception as e: self.log.debug( 'Failed to connect to instance %s with address %s. Caught exception %s' % (srvr_instance.id, address, e)) except Exception as e: self.log.exception( 'Caught exception while polling networks of instance %s: %s' % (srvr_instance.id, e)) return None sleep(5) return None def _create_ipaddr_for_instance(self, srvr_instance): try: address = str(srvr_instance.add_floating_ip().ip) self.log.debug('Using address %s to reach instance %s' % (address, srvr_instance.id)) return address except Exception as e: self.log.exception( 'Caught exception trying to add floating IP to instance %s: %s' % (srvr_instance.id, e)) return None def _enable_root_from_user_with_command_prefix(self, user, cmd_prefix, ip_addr, private_key): for index in range(0, 120, 5): try: enable_root(ip_addr, private_key, user, cmd_prefix) return True except Exception as e: if index < 120: self.log.debug(e) sleep(5) else: return False
class Nova(object): """ Nova implements the ImageFactory OSDelegate interface for the Nova plugin. """ zope.interface.implements(OSDelegate) def __init__(self): super(Nova, self).__init__() self.app_config = ApplicationConfiguration().configuration self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) self.nib = None self._cloud_plugin_content = [] def abort(self): """ Abort the current operation. """ if self.nib and isinstance(self.nib, NIB): status = self.nib.abort() self.log.debug('aborting... status: %s' % status) else: self.log.debug('No active Nova Image Builder instance found, nothing to abort.') def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info('create_base_image() called for Nova plugin - creating a BaseImage') self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update(5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = {'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': False} builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = NIB(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update(10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') os_image_id = self.nib.wait_for_completion(180) if os_image_id: builder.base_image.properties[PROPERTY_NAME_GLANCE_ID] = os_image_id builder.base_image.update(100, 'COMPLETE', 'Image stored in glance with id (%s)' % os_image_id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg) def create_target_image(self, builder, target, base_image, parameters): """ *** NOT YET IMPLEMENTED *** Performs cloud specific customization on the base image. @param builder The builder object. @param base_image The BaseImage to customize. @param target The cloud type to customize for. @param parameters Dictionary of target specific parameters. @return A TargetImage object. """ self.log.info('create_target_image() currently unsupported for Nova plugin') ### TODO: Snapshot the image in glance, launch in nova, and ssh in to customize. # The following is incomplete and not correct as it assumes local manipulation of the image # self.log.info('create_target_image() called for Nova plugin - creating TargetImage') # base_img_path = base_image.data # target_img_path = builder.target_image.data # # builder.target_image.update(status='PENDING', detail='Copying base image...') # if os.path.exists(base_img_path) and os.path.getsize(base_img_path): # try: # shutil.copyfile(base_img_path, target_img_path) # except IOError as e: # builder.target_image.update(status='FAILED', error='Error copying base image: %s' % e) # self.log.exception(e) # raise e # else: # glance_id = base_image.properties[PROPERTY_NAME_GLANCE_ID] # base_img_file = StackEnvironment().download_image_from_glance(glance_id) # with open(builder.target_image.data, 'wb') as target_img_file: # shutil.copyfileobj(base_img_file, target_img_file) # base_img_file.close() def add_cloud_plugin_content(self, content): """ This is a method that cloud plugins can call to deposit content/commands to be run during the OS-specific first stage of the Target Image creation. There is no support for repos at the moment as these introduce external dependencies that we may not be able to resolve. @param content dict containing commands and file. """ self._cloud_plugin_content.append(content)
def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info('create_base_image() called for Nova plugin - creating a BaseImage') self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update(5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = {'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': parameters.get('direct_boot', False), 'timeout': parameters.get('timeout', 1800), 'public': parameters.get('public', False), 'floating_ip': parameters.get('request_floating_ip', False)} builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = Builder(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update(10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') jeos_image_id = self.nib.wait_for_completion(180) if jeos_image_id: builder.base_image.update(30, 'BUILDING', 'JEOS image in glance with id (%s), starting customization...' % jeos_image_id) self.log.debug('Launching Nova instance with image (%s) for customization & icicle generation.' % jeos_image_id) img_name = self.nib.env.glance.images.get(jeos_image_id).name jeos_instance = self.nib.env.launch_instance(name='Customize %s and Generate ICICLE' % img_name, root_disk=jeos_image_id, flavor=parameters.get('flavor', None)) if not jeos_instance: raise ImageFactoryException('Reached timeout waiting for customization instance...') self.log.debug('Launched Nova instance (id: %s) for customization & icicle generation.' % jeos_instance.id) if not jeos_instance.open_ssh(): # Add a security group for ssh access raise ImageFactoryException('Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user') private_key_file = jeos_instance.key_dir + jeos_instance.key_pair.name # Get an IP address to use below for ssh connections to the instance. if self._networking_is_active_for_instance(jeos_instance): if install_config['floating_ip']: jeos_instance_addr = self._create_ipaddr_for_instance(jeos_instance) else: jeos_instance_addr = self._get_ipaddr_for_instance(jeos_instance, user, private_key_file) if not jeos_instance_addr: jeos_instance_addr = self._create_ipaddr_for_instance(jeos_instance) else: raise ImageFactoryException('Networking not active for instance %s, cannot continue!' % jeos_instance.id) if not jeos_instance_addr: raise ImageFactoryException('Unable to obtain an IP address for instance %s' % jeos_instance.id) # Enable root for customization steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix(user, cmd_prefix, jeos_instance_addr, private_key_file): self.log.debug('Temporarily enabled root user for image customization steps...') else: raise ImageFactoryException('Unable to access %s as root. Cannot continue...' % jeos_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException('No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=TDL(str(template)), config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, jeos_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(jeos_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.base_image.update(90, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % jeos_image_id) builder.base_image.icicle = oz_guest.do_icicle(jeos_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(jeos_instance_addr, private_key_file, user, cmd_prefix) self.log.debug('Disabling root ssh access now that customization is complete...') jeos_instance.close_ssh() # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % jeos_instance_addr) if jeos_instance.shutoff(): base_image_id = jeos_instance.create_snapshot(template.name + '-base') builder.base_image.properties[PROPERTY_NAME_GLANCE_ID] = base_image_id builder.base_image.update(100, 'COMPLETE', 'Image stored in glance with id (%s)' % base_image_id) jeos_instance.terminate() else: raise ImageFactoryException('JEOS build instance (%s) never shutdown in Nova.' % jeos_instance.id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg)
class Nova(object): """ Nova implements the ImageFactory OSDelegate interface for the Nova plugin. """ zope.interface.implements(OSDelegate) def __init__(self): super(Nova, self).__init__() self.app_config = ApplicationConfiguration().configuration self.log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__)) self.nib = None self._cloud_plugin_content = [] def abort(self): """ Abort the current operation. """ if self.nib and isinstance(self.nib, Builder): status = self.nib.abort() self.log.debug('aborting... status: %s' % status) else: self.log.debug('No active Nova Image Builder instance found, nothing to abort.') def create_base_image(self, builder, template, parameters): """ Create a JEOS image and install any packages specified in the template. @param builder The Builder object coordinating image creation. @param template A Template object. @param parameters Dictionary of target specific parameters. @return A BaseImage object. """ self.log.info('create_base_image() called for Nova plugin - creating a BaseImage') self.log.debug('Nova.create_base_image() called by builder (%s)' % builder) if not parameters: parameters = {} self.log.debug('parameters set to %s' % parameters) builder.base_image.update(5, 'PENDING', 'Collecting build arguments to pass to Nova Image Builder...') # Derive the OSInfo OS short_id from the os_name and os_version in template if template.os_version: if template.os_name[-1].isdigit(): install_os = '%s.%s' % (template.os_name, template.os_version) else: install_os = '%s%s' % (template.os_name, template.os_version) else: install_os = template.os_name install_os = install_os.lower() install_location = template.install_location # TDL uses 'url' but Nova Image Builder uses 'tree' install_type = 'tree' if template.install_type == 'url' else template.install_type install_script = parameters.get('install_script') install_config = {'admin_password': parameters.get('admin_password'), 'license_key': parameters.get('license_key'), 'arch': template.os_arch, 'disk_size': parameters.get('disk_size'), 'flavor': parameters.get('flavor'), 'storage': parameters.get('storage'), 'name': template.name, 'direct_boot': parameters.get('direct_boot', False), 'timeout': parameters.get('timeout', 1800), 'public': parameters.get('public', False), 'floating_ip': parameters.get('request_floating_ip', False)} builder.base_image.update(10, 'BUILDING', 'Created Nova Image Builder instance...') self.nib = Builder(install_os, install_location, install_type, install_script, install_config) self.nib.run() builder.base_image.update(10, 'BUILDING', 'Waiting for Nova Image Builder to complete...') jeos_image_id = self.nib.wait_for_completion(180) if jeos_image_id: builder.base_image.update(30, 'BUILDING', 'JEOS image in glance with id (%s), starting customization...' % jeos_image_id) self.log.debug('Launching Nova instance with image (%s) for customization & icicle generation.' % jeos_image_id) img_name = self.nib.env.glance.images.get(jeos_image_id).name jeos_instance = self.nib.env.launch_instance(name='Customize %s and Generate ICICLE' % img_name, root_disk=jeos_image_id, flavor=parameters.get('flavor', None)) if not jeos_instance: raise ImageFactoryException('Reached timeout waiting for customization instance...') self.log.debug('Launched Nova instance (id: %s) for customization & icicle generation.' % jeos_instance.id) if not jeos_instance.open_ssh(): # Add a security group for ssh access raise ImageFactoryException('Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user') private_key_file = jeos_instance.key_dir + jeos_instance.key_pair.name # Get an IP address to use below for ssh connections to the instance. if self._networking_is_active_for_instance(jeos_instance): if install_config['floating_ip']: jeos_instance_addr = self._create_ipaddr_for_instance(jeos_instance) else: jeos_instance_addr = self._get_ipaddr_for_instance(jeos_instance, user, private_key_file) if not jeos_instance_addr: jeos_instance_addr = self._create_ipaddr_for_instance(jeos_instance) else: raise ImageFactoryException('Networking not active for instance %s, cannot continue!' % jeos_instance.id) if not jeos_instance_addr: raise ImageFactoryException('Unable to obtain an IP address for instance %s' % jeos_instance.id) # Enable root for customization steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix(user, cmd_prefix, jeos_instance_addr, private_key_file): self.log.debug('Temporarily enabled root user for image customization steps...') else: raise ImageFactoryException('Unable to access %s as root. Cannot continue...' % jeos_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException('No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=TDL(str(template)), config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, jeos_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(jeos_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.base_image.update(90, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % jeos_image_id) builder.base_image.icicle = oz_guest.do_icicle(jeos_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(jeos_instance_addr, private_key_file, user, cmd_prefix) self.log.debug('Disabling root ssh access now that customization is complete...') jeos_instance.close_ssh() # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % jeos_instance_addr) if jeos_instance.shutoff(): base_image_id = jeos_instance.create_snapshot(template.name + '-base') builder.base_image.properties[PROPERTY_NAME_GLANCE_ID] = base_image_id builder.base_image.update(100, 'COMPLETE', 'Image stored in glance with id (%s)' % base_image_id) jeos_instance.terminate() else: raise ImageFactoryException('JEOS build instance (%s) never shutdown in Nova.' % jeos_instance.id) else: exc_msg = 'Nova Image Builder failed to return a Glance ID, failing...' builder.base_image.update(status='FAILED', error=exc_msg) self.log.exception(exc_msg) raise ImageFactoryException(exc_msg) def create_target_image(self, builder, target, base_image, parameters): """ *** NOT YET IMPLEMENTED *** Performs cloud specific customization on the base image. @param builder The builder object. @param base_image The BaseImage to customize. @param target The cloud type to customize for. @param parameters Dictionary of target specific parameters. @return A TargetImage object. """ self.log.info('create_target_image() called for Nova plugin - creating a TargetImage') # Merge together any TDL-style customizations requested via our plugin-to-plugin interface with any target # specific packages, repos and commands and then run a second Oz customization step. tdl = TDL(xmlstring=builder.target_image.template, rootpw_required=self.app_config['tdl_require_root_pw']) # We remove any packages, commands and files from the original TDL - these have already been # installed/executed. We leave the repos in place, as it is possible that the target # specific packages or commands may require them. tdl.packages = [] tdl.commands = {} tdl.files = {} # Get user defined repositories and packages from a local config file repositories, packages = self._target_content(tdl, target) if repositories: tdl.merge_repositories(repositories) if packages: tdl.merge_packages(packages) # Content provided by the target plugin for the target plugin if len(self._cloud_plugin_content) > 0: tdl = self.merge_cloud_content_with_tdl(self._cloud_plugin_content, tdl) # If there are no new commands, packages or files, we can stop here if (len(tdl.packages) + len(tdl.commands) + len(tdl.files)) == 0: self.log.debug('No further modification of the TargetImage to perform in the OS Plugin - returning') return base_image_id = base_image.properties[PROPERTY_NAME_GLANCE_ID] stack_env = StackEnvironment() base_instance = stack_env.launch_instance(name='Target Image Prep', root_disk=('glance', base_image_id), flavor=parameters.get('flavor')) if not base_instance: raise ImageFactoryException('Reached timeout waiting for base instance...') self.log.debug('Launched Nova instance (id: %s) for target specific customization.' % base_instance.id) if not base_instance.open_ssh(): # Add A security group for ssh access raise ImageFactoryException('Failed to add security group for ssh, cannot continue...') user = parameters.get('default_user', 'root') private_key_file = base_instance.key_dir + base_instance.key_pair.name # Get an IP address to use for ssh connections if self._networking_is_active_for_instance(base_instance): if parameters.get('request_floating_ip', False): base_instance_addr = self._create_ipaddr_for_instance(base_instance) else: base_instance_addr = self._get_ipaddr_for_instance(base_instance, user, private_key_file) if not base_instance_addr: base_instance_addr = self._create_ipaddr_for_instance(base_instance) else: raise ImageFactoryException('Networking not active for instance %s, cannot continue!' % base_instance.id) if not base_instance_addr: raise ImageFactoryException('Unable to obtain IP address for instance %s' % base_instance.id) # Enable root for target prep steps cmd_prefix = parameters.get('command_prefix') if user and user != 'root': if self._enable_root_from_user_with_command_prefix(user, cmd_prefix, base_instance_addr, private_key_file): self.log.debug('Temporarily enabled root user for target preparation steps...') else: raise ImageFactoryException('Unable to access %s as root. Cannot continue...' % base_instance_addr) oz_config = self._oz_config(private_key_file) if not oz_config: raise ImageFactoryException('No Oz config file found. Cannot continue.') oz_guest = oz.GuestFactory.guest_factory(tdl=tdl, config=oz_config, auto=None) if self._confirm_ssh_access(oz_guest, base_instance_addr): self.log.debug('Starting base image customization.') oz_guest.do_customize(base_instance_addr) self.log.debug('Completed base image customization.') self.log.debug('Starting ICICLE generation.') builder.target_image.update(85, 'BUILDING', 'Starting ICICLE generation (glance: %s)...' % base_image_id) builder.target_image.icicle = oz_guest.do_icicle(base_instance_addr) self.log.debug('Completed ICICLE generation') # Disable ssh access for root if user and user != 'root': disable_root(base_instance_addr, private_key_file, user, cmd_prefix) self.log.debug('Disabling root ssh access now that customization is complete...') base_instance.close_ssh() # Remove security group for ssh access else: raise ImageFactoryException('Unable to reach %s via ssh.' % base_instance_addr) if base_instance.shutoff(): target_image_id = base_instance.create_snapshot(tdl.name + '-base') builder.target_image.properties[PROPERTY_NAME_GLANCE_ID] = target_image_id builder.target_image.update(90, 'BUILDING', 'Target Image stored in glance with id (%s)' % target_image_id) base_instance.terminate() else: raise ImageFactoryException('JEOS build instance (%s) never shutdown in Nova.' % base_instance.id) builder.target_image.update(95, 'BUILDING', 'Downloading target image...') target_img_download = StackEnvironment().download_image_from_glance(target_image_id) with open(builder.target_image.data, 'wb') as target_img_file: shutil.copyfileobj(target_img_download, target_img_file) target_img_file.close() target_img_download.close() def add_cloud_plugin_content(self, content): """ This is a method that cloud plugins can call to deposit content/commands to be run during the OS-specific first stage of the Target Image creation. There is no support for repos at the moment as these introduce external dependencies that we may not be able to resolve. @param content dict containing commands and file. """ self._cloud_plugin_content.append(content) def merge_cloud_content_with_tdl(self, contents, tdl): """ Merge 'files' and 'commands' content into an existing TDL instance. @param contents: Array of content. @param tdl: TDL instance @return: @raise ImageFactoryException: """ for item in contents: if 'files' in item: for entry in item['files']: if not 'name' in entry: raise ImageFactoryException('File given without a name') if not 'type' in entry: raise ImageFactoryException('File given without a type') if not 'file' in entry: raise ImageFactoryException('File given without any content') if entry['type'] == 'raw': tdl.files[entry['name']] = entry['file'] elif entry['type'] == 'base64': if len(entry['file']) == 0: self.log.warning('File given with zero length... %s' % entry['name']) tdl.files[entry['name']] = '' else: tdl.files[entry['name']] = b64decode(entry['file']) else: raise ImageFactoryException('File given with invalid type (%s)' % entry['type']) if 'commands' in item: for entry in item['commands']: if not 'name' in entry: raise ImageFactoryException('Command given without a name') if not 'type' in entry: raise ImageFactoryException('Command given without a type') if not 'command' in entry: raise ImageFactoryException('Command given without any content') if entry['type'] == 'raw': tdl.commands[entry['name']] = entry['command'] elif entry['type'] == 'base64': if len(entry['command']) == 0: self.log.warning('Command given with zero length... %s' % entry['name']) tdl.commands[entry['name']] = '' else: tdl.commands[entry['name']] = b64decode(entry['command']) else: raise ImageFactoryException('Command given with invalid type (%s)' % entry['type']) return tdl def _target_content(self, tdl, target): target_xml = '/etc/imagefactory/target_content.xml' if os.path.isfile(target_xml): doc = libxml2.parseFile(target_xml) else: self.log.debug("Found neither a call-time config nor a config file - doing nothing") return None, None # We go from most to least specific in this order: # arch -> version -> os-> target # Note that at the moment we even allow an include statment that covers absolutely everything. # That is, one that doesn't even specify a target - this is to support a very simple call-time syntax include = doc.xpathEval("/template_includes/include[@target='%s' and @os='%s' and @version='%s' and @arch='%s']" % (target, tdl.distro, tdl.update, tdl.arch)) if len(include) == 0: include = doc.xpathEval("/template_includes/include[@target='%s' and @os='%s' and @version='%s' and \ not(@arch)]" % (target, tdl.distro, tdl.update)) if len(include) == 0: include = doc.xpathEval("/template_includes/include[@target='%s' and @os='%s' and not(@version) and \ not(@arch)]" % (target, tdl.distro)) if len(include) == 0: include = doc.xpathEval("/template_includes/include[@target='%s' and not(@os) and not(@version) and \ not(@arch)]" % target) if len(include) == 0: include = doc.xpathEval("/template_includes/include[not(@target) and not(@os) and not(@version) and \ not(@arch)]") if len(include) == 0: self.log.debug("cannot find a config section that matches our build details - doing nothing") return None, None # OK - We have at least one config block that matches our build - take the first one, merge it and be done # TODO: Merge all of them? Err out if there is more than one? Warn? include = include[0] packages = include.xpathEval("packages") if len(packages) > 0: ret_pkgs = packages[0] else: ret_pkgs = None repositories = include.xpathEval("repositories") if len(repositories) > 0: ret_repos = repositories[0] else: ret_repos = None return ret_repos, ret_pkgs def _confirm_ssh_access(self, guest, addr, timeout=300): confirmation = False for index in range(timeout/10): if index % 10 == 0: self.log.debug('Checking ssh access to %s - %d' % (addr, index)) try: guest.guest_execute_command(addr, '/bin/true', timeout=10) confirmation = True break except Exception as e: self.log.exception('Caught exception while checking ssh access to %s: %s' % (addr, e)) sleep(1) if not confirmation: self.log.debug('Unable to confirm ssh access to %s after %s minutes...' % (addr, timeout/60)) return confirmation def _oz_config(self, private_key_file): config = SafeConfigParser() if config.read("/etc/oz/oz.cfg"): config.set('paths', 'output_dir', self.app_config['imgdir']) config.set('paths', 'sshprivkey', private_key_file) if 'oz_data_dir' in self.app_config: config.set('paths', 'data_dir', self.app_config['oz_data_dir']) if 'oz_screenshot_dir' in self.app_config: config.set('paths', 'screenshot_dir', self.app_config['oz_screenshot_dir']) return config else: return None def _networking_is_active_for_instance(self, srvr_instance): for index in range(0, 120, 5): self.log.debug('Polling for networks associated with instance %s' % srvr_instance.id) if len(srvr_instance.instance.networks) > 0: return True else: sleep(5) return False def _get_ipaddr_for_instance(self, srvr_instance, user, key): for index in range(0, 300, 5): try: for network in srvr_instance.instance.networks.values(): for address in network: try: stdout, stderr, retcode = ssh_execute_command(str(address), key, '/bin/id', user=user) if retcode == 0: self.log.debug('Connected to %s' % address) return address except Exception as e: self.log.debug('Failed to connect to instance %s with address %s. Caught exception %s' % (srvr_instance.id, address, e)) except Exception as e: self.log.exception('Caught exception while polling networks of instance %s: %s' % (srvr_instance.id, e)) return None sleep(5) return None def _create_ipaddr_for_instance(self, srvr_instance): try: address = str(srvr_instance.add_floating_ip().ip) self.log.debug('Using address %s to reach instance %s' % (address, srvr_instance.id)) return address except Exception as e: self.log.exception('Caught exception trying to add floating IP to instance %s: %s' % (srvr_instance.id, e)) return None def _enable_root_from_user_with_command_prefix(self, user, cmd_prefix, ip_addr, private_key): for index in range(0, 120, 5): try: enable_root(ip_addr, private_key, user, cmd_prefix) return True except Exception as e: if index < 120: self.log.debug(e) sleep(5) else: return False