def mkfs(fs, path, label=None): """Format a file or block device :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4' 'btrfs', etc.) :param path: Path to file or block device to format :param label: Volume label to use """ if fs == 'swap': args = ['mkswap'] else: args = ['mkfs', '-t', fs] # add -F to force no interactive execute on non-block device. if fs in ('ext3', 'ext4'): args.extend(['-F']) if label: if fs in ('msdos', 'vfat'): label_opt = '-n' else: label_opt = '-L' args.extend([label_opt, label]) args.append(path) try: execute(*args, run_as_root=True, use_standard_locale=True) except processutils.ProcessExecutionError as e: with excutils.save_and_reraise_exception() as ctx: if os.strerror(errno.ENOENT) in e.stderr: ctx.reraise = False LOG.exception(_LE('Failed to make file system. ' 'File system %s is not supported.'), fs) raise exception.FileSystemNotSupported(fs=fs) else: LOG.exception(_LE('Failed to create a file system ' 'in %(path)s. Error: %(error)s'), {'path': path, 'error': e})
def _mount_deploy_iso(deploy_iso, mountdir): """This function opens up the deploy iso used for deploy. :param: deploy_iso: path to the deploy iso where its contents are fetched to. :raises: ImageCreationFailed if mount fails. :returns: a tuple consisting of - 1. a dictionary containing the values as required by create_isolinux_image, 2. efiboot.img relative path, and 3. grub.cfg relative path. """ e_img_rel_path = None e_img_path = None grub_rel_path = None grub_path = None try: utils.mount(deploy_iso, mountdir, '-o', 'loop') except processutils.ProcessExecutionError as e: LOG.exception(_LE("mounting the deploy iso failed.")) raise exception.ImageCreationFailed(image_type='iso', error=e) try: for (dir, subdir, files) in os.walk(mountdir): if 'efiboot.img' in files: e_img_path = os.path.join(dir, 'efiboot.img') e_img_rel_path = os.path.relpath(e_img_path, mountdir) if 'grub.cfg' in files: grub_path = os.path.join(dir, 'grub.cfg') grub_rel_path = os.path.relpath(grub_path, mountdir) except (OSError, IOError) as e: LOG.exception(_LE("examining the deploy iso failed.")) _umount_without_raise(mountdir) raise exception.ImageCreationFailed(image_type='iso', error=e) # check if the variables are assigned some values or not during # walk of the mountdir. if not (e_img_path and e_img_rel_path and grub_path and grub_rel_path): error = (_("Deploy iso didn't contain efiboot.img or grub.cfg")) _umount_without_raise(mountdir) raise exception.ImageCreationFailed(image_type='iso', error=error) uefi_path_info = {e_img_path: e_img_rel_path, grub_path: grub_rel_path} # Returning a tuple as it makes the code simpler and clean. # uefi_path_info: is needed by the caller for _create_root_fs to create # appropriate directory structures for uefi boot iso. # grub_rel_path: is needed to copy the new grub.cfg generated using # generate_cfg() to the same directory path structure where it was # present in deploy iso. This path varies for different OS vendors. # e_img_rel_path: is required by mkisofs to generate boot iso. return uefi_path_info, e_img_rel_path, grub_rel_path
def create_isolinux_image_for_bios(output_file, kernel, ramdisk, kernel_params=None): """Creates an isolinux image on the specified file. Copies the provided kernel, ramdisk to a directory, generates the isolinux configuration file using the kernel parameters provided, and then generates a bootable ISO image. :param output_file: the path to the file where the iso image needs to be created. :param kernel: the kernel to use. :param ramdisk: the ramdisk to use. :param kernel_params: a list of strings(each element being a string like 'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added as the kernel cmdline. :raises: ImageCreationFailed, if image creation failed while copying files or while running command to generate iso. """ ISOLINUX_BIN = 'isolinux/isolinux.bin' ISOLINUX_CFG = 'isolinux/isolinux.cfg' options = {'kernel': '/vmlinuz', 'ramdisk': '/initrd'} with utils.tempdir() as tmpdir: files_info = { kernel: 'vmlinuz', ramdisk: 'initrd', CONF.isolinux_bin: ISOLINUX_BIN, } try: _create_root_fs(tmpdir, files_info) except (OSError, IOError) as e: LOG.exception(_LE("Creating the filesystem root failed.")) raise exception.ImageCreationFailed(image_type='iso', error=e) cfg = _generate_cfg(kernel_params, CONF.isolinux_config_template, options) isolinux_cfg = os.path.join(tmpdir, ISOLINUX_CFG) utils.write_to_file(isolinux_cfg, cfg) try: utils.execute('mkisofs', '-r', '-V', "VMEDIA_BOOT_ISO", '-cache-inodes', '-J', '-l', '-no-emul-boot', '-boot-load-size', '4', '-boot-info-table', '-b', ISOLINUX_BIN, '-o', output_file, tmpdir) except processutils.ProcessExecutionError as e: LOG.exception(_LE("Creating ISO image failed.")) raise exception.ImageCreationFailed(image_type='iso', error=e)
def __init__(self, message=None, **kwargs): self.kwargs = kwargs if 'code' not in self.kwargs: try: self.kwargs['code'] = self.code except AttributeError: pass if not message: try: message = self.message % kwargs except Exception as e: # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception(_LE('Exception in string format operation')) for name, value in kwargs.items(): LOG.error("%s: %s" % (name, value)) if CONF.fatal_exception_format_errors: raise e else: # at least get the core message out if something happened message = self.message super(IotronicException, self).__init__(message)
def obj_class_from_name(cls, objname, objver): """Returns a class from the registry based on a name and version.""" if objname not in cls._obj_classes: LOG.error( _LE('Unable to instantiate unregistered object type ' '%(objtype)s'), dict(objtype=objname)) raise exception.UnsupportedObjectError(objtype=objname) latest = None compatible_match = None for objclass in cls._obj_classes[objname]: if objclass.VERSION == objver: return objclass version_bits = tuple([int(x) for x in objclass.VERSION.split(".")]) if latest is None: latest = version_bits elif latest < version_bits: latest = version_bits if versionutils.is_compatible(objver, objclass.VERSION): compatible_match = objclass if compatible_match: return compatible_match latest_ver = '%i.%i' % latest raise exception.IncompatibleObjectVersion(objname=objname, objver=objver, supported=latest_ver)
def obj_class_from_name(cls, objname, objver): """Returns a class from the registry based on a name and version.""" if objname not in cls._obj_classes: LOG.error(_LE('Unable to instantiate unregistered object type ' '%(objtype)s'), dict(objtype=objname)) raise exception.UnsupportedObjectError(objtype=objname) latest = None compatible_match = None for objclass in cls._obj_classes[objname]: if objclass.VERSION == objver: return objclass version_bits = tuple([int(x) for x in objclass.VERSION.split(".")]) if latest is None: latest = version_bits elif latest < version_bits: latest = version_bits if versionutils.is_compatible(objver, objclass.VERSION): compatible_match = objclass if compatible_match: return compatible_match latest_ver = '%i.%i' % latest raise exception.IncompatibleObjectVersion(objname=objname, objver=objver, supported=latest_ver)
def create_vfat_image(output_file, files_info=None, parameters=None, parameters_file='parameters.txt', fs_size_kib=100): """Creates the fat fs image on the desired file. This method copies the given files to a root directory (optional), writes the parameters specified to the parameters file within the root directory (optional), and then creates a vfat image of the root directory. :param output_file: The path to the file where the fat fs image needs to be created. :param files_info: A dict containing absolute path of file to be copied -> relative path within the vfat image. For example, { '/absolute/path/to/file' -> 'relative/path/within/root' ... } :param parameters: A dict containing key-value pairs of parameters. :param parameters_file: The filename for the parameters file. :param fs_size_kib: size of the vfat filesystem in KiB. :raises: ImageCreationFailed, if image creation failed while doing any of filesystem manipulation activities like creating dirs, mounting, creating filesystem, copying files, etc. """ try: utils.dd('/dev/zero', output_file, 'count=1', "bs=%dKiB" % fs_size_kib) except processutils.ProcessExecutionError as e: raise exception.ImageCreationFailed(image_type='vfat', error=e) with utils.tempdir() as tmpdir: try: # The label helps ramdisks to find the partition containing # the parameters (by using /dev/disk/by-label/ir-vfd-dev). # NOTE: FAT filesystem label can be up to 11 characters long. utils.mkfs('vfat', output_file, label="ir-vfd-dev") utils.mount(output_file, tmpdir, '-o', 'umask=0') except processutils.ProcessExecutionError as e: raise exception.ImageCreationFailed(image_type='vfat', error=e) try: if files_info: _create_root_fs(tmpdir, files_info) if parameters: parameters_file = os.path.join(tmpdir, parameters_file) params_list = ['%(key)s=%(val)s' % {'key': k, 'val': v} for k, v in parameters.items()] file_contents = '\n'.join(params_list) utils.write_to_file(parameters_file, file_contents) except Exception as e: LOG.exception(_LE("vfat image creation failed. Error: %s"), e) raise exception.ImageCreationFailed(image_type='vfat', error=e) finally: try: utils.umount(tmpdir) except processutils.ProcessExecutionError as e: raise exception.ImageCreationFailed(image_type='vfat', error=e)
def setter(self, value, name=name, typefn=typefn): self._changed_fields.add(name) try: return setattr(self, get_attrname(name), typefn(value)) except Exception: attr = "%s.%s" % (self.obj_name(), name) LOG.exception(_LE('Error setting %(attr)s'), {'attr': attr}) raise
def stop(self): try: self.rpcserver.stop() self.rpcserver.wait() except Exception as e: LOG.exception(_LE('Service error occurred when stopping the ' 'RPC server. Error: %s'), e) try: self.manager.del_host(deregister=self.deregister) except Exception as e: LOG.exception(_LE('Service error occurred when cleaning up ' 'the RPC manager. Error: %s'), e) super(RPCService, self).stop(graceful=True) LOG.info(_LI('Stopped RPC server for service %(service)s on host ' '%(host)s.'), {'service': self.topic, 'host': self.host})
def __call__(self, environ, start_response): # Request for this state, modified by replace_start_response() # and used when an error is being reported. state = {} def replacement_start_response(status, headers, exc_info=None): """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) state['status_code'] = status_code except (ValueError, TypeError): # pragma: nocover raise Exception( _('ErrorDocumentMiddleware received an invalid ' 'status %s') % status) else: if (state['status_code'] // 100) not in (2, 3): # Remove some headers so we can replace them later # when we have the full error message and can # compute the length. headers = [(h, v) for (h, v) in headers if h not in ('Content-Length', 'Content-Type')] # Save the headers in case we need to modify them. state['headers'] = headers return start_response(status, headers, exc_info) app_iter = self.app(environ, replacement_start_response) if (state['status_code'] // 100) not in (2, 3): req = webob.Request(environ) if (req.accept.best_match(['application/json', 'application/xml' ]) == 'application/xml'): try: # simple check xml is valid body = [ et.ElementTree.tostring( et.ElementTree.fromstring('<error_message>' + '\n'.join(app_iter) + '</error_message>')) ] except et.ElementTree.ParseError as err: LOG.error(_LE('Error parsing HTTP response: %s'), err) body = [ '<error_message>%s' % state['status_code'] + '</error_message>' ] state['headers'].append(('Content-Type', 'application/xml')) else: if six.PY3: app_iter = [i.decode('utf-8') for i in app_iter] body = [json.dumps({'error_message': '\n'.join(app_iter)})] if six.PY3: body = [item.encode('utf-8') for item in body] state['headers'].append(('Content-Type', 'application/json')) state['headers'].append(('Content-Length', str(len(body[0])))) else: body = app_iter return body
def tempdir(**kwargs): tempfile.tempdir = CONF.tempdir tmpdir = tempfile.mkdtemp(**kwargs) try: yield tmpdir finally: try: shutil.rmtree(tmpdir) except OSError as e: LOG.error(_LE('Could not remove tmpdir: %s'), e)
def __call__(self, environ, start_response): # Request for this state, modified by replace_start_response() # and used when an error is being reported. state = {} def replacement_start_response(status, headers, exc_info=None): """Overrides the default response to make errors parsable.""" try: status_code = int(status.split(' ')[0]) state['status_code'] = status_code except (ValueError, TypeError): # pragma: nocover raise Exception(_( 'ErrorDocumentMiddleware received an invalid ' 'status %s') % status) else: if (state['status_code'] // 100) not in (2, 3): # Remove some headers so we can replace them later # when we have the full error message and can # compute the length. headers = [(h, v) for (h, v) in headers if h not in ('Content-Length', 'Content-Type') ] # Save the headers in case we need to modify them. state['headers'] = headers return start_response(status, headers, exc_info) app_iter = self.app(environ, replacement_start_response) if (state['status_code'] // 100) not in (2, 3): req = webob.Request(environ) if (req.accept.best_match( ['application/json', 'application/xml']) == 'application/xml'): try: # simple check xml is valid body = [et.ElementTree.tostring( et.ElementTree.fromstring('<error_message>' + '\n'.join(app_iter) + '</error_message>'))] except et.ElementTree.ParseError as err: LOG.error(_LE('Error parsing HTTP response: %s'), err) body = ['<error_message>%s' % state['status_code'] + '</error_message>'] state['headers'].append(('Content-Type', 'application/xml')) else: if six.PY3: app_iter = [i.decode('utf-8') for i in app_iter] body = [json.dumps({'error_message': '\n'.join(app_iter)})] if six.PY3: body = [item.encode('utf-8') for item in body] state['headers'].append(('Content-Type', 'application/json')) state['headers'].append(('Content-Length', str(len(body[0])))) else: body = app_iter return body
def mkfs(fs, path, label=None): """Format a file or block device :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4' 'btrfs', etc.) :param path: Path to file or block device to format :param label: Volume label to use """ if fs == 'swap': args = ['mkswap'] else: args = ['mkfs', '-t', fs] # add -F to force no interactive execute on non-block device. if fs in ('ext3', 'ext4'): args.extend(['-F']) if label: if fs in ('msdos', 'vfat'): label_opt = '-n' else: label_opt = '-L' args.extend([label_opt, label]) args.append(path) try: execute(*args, run_as_root=True, use_standard_locale=True) except processutils.ProcessExecutionError as e: with excutils.save_and_reraise_exception() as ctx: if os.strerror(errno.ENOENT) in e.stderr: ctx.reraise = False LOG.exception( _LE('Failed to make file system. ' 'File system %s is not supported.'), fs) raise exception.FileSystemNotSupported(fs=fs) else: LOG.exception( _LE('Failed to create a file system ' 'in %(path)s. Error: %(error)s'), { 'path': path, 'error': e })
def call(self, method, *args, **kwargs): """Call a glance client method. If we get a connection error, retry the request according to CONF.glance_num_retries. :param context: The request context, for access checks. :param version: The requested API version.v :param method: The method requested to be called. :param args: A list of positional arguments for the method called :param kwargs: A dict of keyword arguments for the method called :raises: GlanceConnectionFailed """ retry_excs = (glance_exc.ServiceUnavailable, glance_exc.InvalidEndpoint, glance_exc.CommunicationError) image_excs = (glance_exc.Forbidden, glance_exc.Unauthorized, glance_exc.NotFound, glance_exc.BadRequest) num_attempts = 1 + CONF.glance.glance_num_retries for attempt in range(1, num_attempts + 1): try: return getattr(self.client.images, method)(*args, **kwargs) except retry_excs as e: host = self.glance_host port = self.glance_port error_msg = _LE("Error contacting glance server " "'%(host)s:%(port)s' for '%(method)s', attempt" " %(attempt)s of %(num_attempts)s failed.") LOG.exception(error_msg, {'host': host, 'port': port, 'num_attempts': num_attempts, 'attempt': attempt, 'method': method}) if attempt == num_attempts: raise exception.GlanceConnectionFailed(host=host, port=port, reason=str(e)) time.sleep(1) except image_excs as e: exc_type, exc_value, exc_trace = sys.exc_info() if method == 'list': new_exc = _translate_plain_exception( exc_value) else: new_exc = _translate_image_exception( args[0], exc_value) six.reraise(type(new_exc), new_exc, exc_trace)
def create_isolinux_image_for_uefi(output_file, deploy_iso, kernel, ramdisk, kernel_params=None): """Creates an isolinux image on the specified file. Copies the provided kernel, ramdisk, efiboot.img to a directory, creates the path for grub config file, generates the isolinux configuration file using the kernel parameters provided, generates the grub configuration file using kernel parameters and then generates a bootable ISO image for uefi. :param output_file: the path to the file where the iso image needs to be created. :param deploy_iso: deploy iso used to initiate the deploy. :param kernel: the kernel to use. :param ramdisk: the ramdisk to use. :param kernel_params: a list of strings(each element being a string like 'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added as the kernel cmdline. :raises: ImageCreationFailed, if image creation failed while copying files or while running command to generate iso. """ ISOLINUX_BIN = 'isolinux/isolinux.bin' ISOLINUX_CFG = 'isolinux/isolinux.cfg' isolinux_options = {'kernel': '/vmlinuz', 'ramdisk': '/initrd'} grub_options = {'linux': '/vmlinuz', 'initrd': '/initrd'} with utils.tempdir() as tmpdir: files_info = { kernel: 'vmlinuz', ramdisk: 'initrd', CONF.isolinux_bin: ISOLINUX_BIN, } # Open the deploy iso used to initiate deploy and copy the # efiboot.img i.e. boot loader to the current temporary # directory. with utils.tempdir() as mountdir: uefi_path_info, e_img_rel_path, grub_rel_path = ( _mount_deploy_iso(deploy_iso, mountdir)) # if either of these variables are not initialized then the # uefi efiboot.img cannot be created. files_info.update(uefi_path_info) try: _create_root_fs(tmpdir, files_info) except (OSError, IOError) as e: LOG.exception(_LE("Creating the filesystem root failed.")) raise exception.ImageCreationFailed(image_type='iso', error=e) finally: _umount_without_raise(mountdir) cfg = _generate_cfg(kernel_params, CONF.isolinux_config_template, isolinux_options) isolinux_cfg = os.path.join(tmpdir, ISOLINUX_CFG) utils.write_to_file(isolinux_cfg, cfg) # Generate and copy grub config file. grub_cfg = os.path.join(tmpdir, grub_rel_path) grub_conf = _generate_cfg(kernel_params, CONF.grub_config_template, grub_options) utils.write_to_file(grub_cfg, grub_conf) # Create the boot_iso. try: utils.execute('mkisofs', '-r', '-V', "VMEDIA_BOOT_ISO", '-cache-inodes', '-J', '-l', '-no-emul-boot', '-boot-load-size', '4', '-boot-info-table', '-b', ISOLINUX_BIN, '-eltorito-alt-boot', '-e', e_img_rel_path, '-no-emul-boot', '-o', output_file, tmpdir) except processutils.ProcessExecutionError as e: LOG.exception(_LE("Creating ISO image failed.")) raise exception.ImageCreationFailed(image_type='iso', error=e)