def _create_lpar_instance(instance): host_stats = self.get_host_stats(refresh=True) inst_name = instance['name'] # CPU/Memory min and max can be configurable. Lets assume # some default values for now. # Memory mem = instance['memory_mb'] if mem > host_stats['host_memory_free']: LOG.error(_('Not enough free memory in the host')) raise exception.PowerVMInsufficientFreeMemory( instance_name=instance['name']) mem_min = min(mem, constants.POWERVM_MIN_MEM) mem_max = mem + constants.POWERVM_MAX_MEM # CPU cpus = instance['vcpus'] avail_cpus = host_stats['vcpus'] - host_stats['vcpus_used'] if cpus > avail_cpus: LOG.error(_('Insufficient available CPU on PowerVM')) raise exception.PowerVMInsufficientCPU( instance_name=instance['name']) cpus_min = min(cpus, constants.POWERVM_MIN_CPUS) cpus_max = cpus + constants.POWERVM_MAX_CPUS cpus_units_min = decimal.Decimal(cpus_min) / decimal.Decimal(10) cpus_units = decimal.Decimal(cpus) / decimal.Decimal(10) try: # Network eth_id = self._operator.get_virtual_eth_adapter_id() # LPAR configuration data lpar_inst = LPAR.LPAR(name=inst_name, lpar_env='aixlinux', min_mem=mem_min, desired_mem=mem, max_mem=mem_max, proc_mode='shared', sharing_mode='uncap', min_procs=cpus_min, desired_procs=cpus, max_procs=cpus_max, min_proc_units=cpus_units_min, desired_proc_units=cpus_units, max_proc_units=cpus_max, virtual_eth_adapters='4/0/%s//0/0' % eth_id) LOG.debug(_("Creating LPAR instance '%s'") % instance['name']) self._operator.create_lpar(lpar_inst) except nova_exception.ProcessExecutionError: LOG.exception( _("LPAR instance '%s' creation failed") % instance['name']) raise exception.PowerVMLPARCreationFailed()
class PowerVMOperator(object): """PowerVM main operator. The PowerVMOperator is intended to wrap all operations from the driver and handle either IVM or HMC managed systems. """ def __init__(self): self._operator = get_powervm_operator() self._disk_adapter = get_powervm_disk_adapter() self._host_stats = {} self._update_host_stats() def get_info(self, instance_name): """Get the current status of an LPAR instance. Returns a dict containing: :state: the running state, one of the power_state codes :max_mem: (int) the maximum memory in KBytes allowed :mem: (int) the memory in KBytes used by the domain :num_cpu: (int) the number of virtual CPUs for the domain :cpu_time: (int) the CPU time used in nanoseconds :raises: PowerVMLPARInstanceNotFound """ lpar_instance = self._get_instance(instance_name) state = constants.POWERVM_POWER_STATE.get(lpar_instance['state'], power_state.NOSTATE) return { 'state': state, 'max_mem': lpar_instance['max_mem'], 'mem': lpar_instance['desired_mem'], 'num_cpu': lpar_instance['max_procs'], 'cpu_time': lpar_instance['uptime'] } def instance_exists(self, instance_name): lpar_instance = self._operator.get_lpar(instance_name) return True if lpar_instance else False def _get_instance(self, instance_name): """Check whether or not the LPAR instance exists and return it.""" lpar_instance = self._operator.get_lpar(instance_name) if lpar_instance is None: LOG.error(_("LPAR instance '%s' not found") % instance_name) raise exception.PowerVMLPARInstanceNotFound( instance_name=instance_name) return lpar_instance def list_instances(self): """ Return the names of all the instances known to the virtualization layer, as a list. """ lpar_instances = self._operator.list_lpar_instances() return lpar_instances def get_available_resource(self): """Retrieve resource info. :returns: dictionary containing resource info """ data = self.get_host_stats() # Memory data is in MB already. memory_mb_used = data['host_memory_total'] - data['host_memory_free'] # Convert to GB local_gb = data['disk_total'] / 1024 local_gb_used = data['disk_used'] / 1024 dic = { 'vcpus': data['vcpus'], 'memory_mb': data['host_memory_total'], 'local_gb': local_gb, 'vcpus_used': data['vcpus_used'], 'memory_mb_used': memory_mb_used, 'local_gb_used': local_gb_used, 'hypervisor_type': data['hypervisor_type'], 'hypervisor_version': data['hypervisor_version'], 'hypervisor_hostname': self._operator.get_hostname(), 'cpu_info': ','.join(data['cpu_info']), 'disk_available_least': data['disk_total'] } return dic def get_host_stats(self, refresh=False): """Return currently known host stats.""" if refresh: self._update_host_stats() return self._host_stats def _update_host_stats(self): memory_info = self._operator.get_memory_info() cpu_info = self._operator.get_cpu_info() # Note: disk avail information is not accurate. The value # is a sum of all Volume Groups and the result cannot # represent the real possibility. Example: consider two # VGs both 10G, the avail disk will be 20G however, # a 15G image does not fit in any VG. This can be improved # later on. disk_info = self._operator.get_disk_info() data = {} data['vcpus'] = cpu_info['total_procs'] data['vcpus_used'] = cpu_info['total_procs'] - cpu_info['avail_procs'] data['cpu_info'] = constants.POWERVM_CPU_INFO data['disk_total'] = disk_info['disk_total'] data['disk_used'] = disk_info['disk_used'] data['disk_available'] = disk_info['disk_avail'] data['host_memory_total'] = memory_info['total_mem'] data['host_memory_free'] = memory_info['avail_mem'] data['hypervisor_type'] = constants.POWERVM_HYPERVISOR_TYPE data['hypervisor_version'] = constants.POWERVM_HYPERVISOR_VERSION data['hypervisor_hostname'] = self._operator.get_hostname() data['extres'] = '' self._host_stats = data def spawn(self, context, instance, image_id, network_info): def _create_image(context, instance, image_id): """Fetch image from glance and copy it to the remote system.""" try: root_volume = self._disk_adapter.create_volume_from_image( context, instance, image_id) self._disk_adapter.attach_volume_to_host(root_volume) lpar_id = self._operator.get_lpar(instance['name'])['lpar_id'] vhost = self._operator.get_vhost_by_instance_id(lpar_id) self._operator.attach_disk_to_vhost(root_volume['device_name'], vhost) except Exception, e: LOG.exception(_("PowerVM image creation failed: %s") % str(e)) raise exception.PowerVMImageCreationFailed() spawn_start = time.time() try: try: host_stats = self.get_host_stats(refresh=True) lpar_inst = self._create_lpar_instance(instance, network_info, host_stats) #TODO(mjfork) capture the error and handle the error when the # MAC prefix already exists on the # system (1 in 2^28) self._operator.create_lpar(lpar_inst) LOG.debug(_("Creating LPAR instance '%s'") % instance['name']) except nova_exception.ProcessExecutionError: LOG.exception( _("LPAR instance '%s' creation failed") % instance['name']) raise exception.PowerVMLPARCreationFailed() _create_image(context, instance, image_id) LOG.debug( _("Activating the LPAR instance '%s'") % instance['name']) self._operator.start_lpar(instance['name']) # TODO(mrodden): probably do this a better way # that actually relies on the time module # and nonblocking threading # Wait for boot timeout_count = range(10) while timeout_count: state = self.get_info(instance['name'])['state'] if state == power_state.RUNNING: LOG.info(_("Instance spawned successfully."), instance=instance) break timeout_count.pop() if len(timeout_count) == 0: LOG.error( _("Instance '%s' failed to boot") % instance['name']) self._cleanup(instance['name']) break time.sleep(1) except exception.PowerVMImageCreationFailed: with excutils.save_and_reraise_exception(): # log errors in cleanup try: self._cleanup(instance['name']) except Exception: LOG.exception( _('Error while attempting to ' 'clean up failed instance launch.')) spawn_time = time.time() - spawn_start LOG.info(_("Instance spawned in %s seconds") % spawn_time, instance=instance)
def _create_lpar_instance(instance): host_stats = self.get_host_stats(refresh=True) inst_name = instance['name'] # CPU/Memory min and max can be configurable. Lets assume # some default values for now. # Memory mem = instance['memory_mb'] if mem > host_stats['host_memory_free']: LOG.error(_('Not enough free memory in the host')) raise exception.PowerVMInsufficientFreeMemory( instance_name=instance['name']) mem_min = min(mem, constants.POWERVM_MIN_MEM) mem_max = mem + constants.POWERVM_MAX_MEM # CPU cpus = instance['vcpus'] avail_cpus = host_stats['vcpus'] - host_stats['vcpus_used'] if cpus > avail_cpus: LOG.error(_('Insufficient available CPU on PowerVM')) raise exception.PowerVMInsufficientCPU( instance_name=instance['name']) cpus_min = min(cpus, constants.POWERVM_MIN_CPUS) cpus_max = cpus + constants.POWERVM_MAX_CPUS cpus_units_min = decimal.Decimal(cpus_min) / decimal.Decimal(10) cpus_units = decimal.Decimal(cpus) / decimal.Decimal(10) try: # Network # To ensure the MAC address on the guest matches the # generated value, pull the first 10 characters off the # MAC address for the mac_base_value parameter and then # get the integer value of the final 2 characters as the # slot_id parameter mac = network_info[0]['address'] mac_base_value = (mac[:-2]).replace(':', '') eth_id = self._operator.get_virtual_eth_adapter_id() slot_id = int(mac[-2:], 16) virtual_eth_adapters = ('%(slot_id)s/0/%(eth_id)s//0/0' % locals()) # LPAR configuration data # max_virtual_slots is hardcoded to 64 since we generate a MAC # address that must be placed in slots 32 - 64 lpar_inst = LPAR.LPAR( name=inst_name, lpar_env='aixlinux', min_mem=mem_min, desired_mem=mem, max_mem=mem_max, proc_mode='shared', sharing_mode='uncap', min_procs=cpus_min, desired_procs=cpus, max_procs=cpus_max, min_proc_units=cpus_units_min, desired_proc_units=cpus_units, max_proc_units=cpus_max, virtual_eth_mac_base_value=mac_base_value, max_virtual_slots=64, virtual_eth_adapters=virtual_eth_adapters) LOG.debug(_("Creating LPAR instance '%s'") % instance['name']) self._operator.create_lpar(lpar_inst) #TODO(mjfork) capture the error and handle the error when the MAC # prefix already exists on the system (1 in 2^28) except nova_exception.ProcessExecutionError: LOG.exception(_("LPAR instance '%s' creation failed") % instance['name']) raise exception.PowerVMLPARCreationFailed()
def spawn(self, context, instance, image_id, network_info): def _create_image(context, instance, image_id): """Fetch image from glance and copy it to the remote system.""" try: root_volume = self._disk_adapter.create_volume_from_image( context, instance, image_id) self._disk_adapter.attach_volume_to_host(root_volume) lpar_id = self._operator.get_lpar(instance['name'])['lpar_id'] vhost = self._operator.get_vhost_by_instance_id(lpar_id) self._operator.attach_disk_to_vhost(root_volume['device_name'], vhost) except Exception as e: LOG.exception(_("PowerVM image creation failed: %s") % str(e)) raise exception.PowerVMImageCreationFailed() spawn_start = time.time() try: try: host_stats = self.get_host_stats(refresh=True) lpar_inst = self._create_lpar_instance(instance, network_info, host_stats) #TODO(mjfork) capture the error and handle the error when the # MAC prefix already exists on the # system (1 in 2^28) self._operator.create_lpar(lpar_inst) LOG.debug(_("Creating LPAR instance '%s'") % instance['name']) except processutils.ProcessExecutionError: LOG.exception( _("LPAR instance '%s' creation failed") % instance['name']) raise exception.PowerVMLPARCreationFailed( instance_name=instance['name']) _create_image(context, instance, image_id) LOG.debug( _("Activating the LPAR instance '%s'") % instance['name']) self._operator.start_lpar(instance['name']) # TODO(mrodden): probably do this a better way # that actually relies on the time module # and nonblocking threading # Wait for boot timeout_count = range(10) while timeout_count: state = self.get_info(instance['name'])['state'] if state == power_state.RUNNING: LOG.info(_("Instance spawned successfully."), instance=instance) break timeout_count.pop() if len(timeout_count) == 0: LOG.error( _("Instance '%s' failed to boot") % instance['name']) self._cleanup(instance['name']) break time.sleep(1) except exception.PowerVMImageCreationFailed: with excutils.save_and_reraise_exception(): # log errors in cleanup try: self._cleanup(instance['name']) except Exception: LOG.exception( _('Error while attempting to ' 'clean up failed instance launch.')) spawn_time = time.time() - spawn_start LOG.info(_("Instance spawned in %s seconds") % spawn_time, instance=instance)