def _update_host_oam_address(host, interface): if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: address_name = cutils.format_address_name( constants.CONTROLLER_HOSTNAME, constants.NETWORK_TYPE_OAM) else: address_name = cutils.format_address_name(host.hostname, constants.NETWORK_TYPE_OAM) address = pecan.request.dbapi.address_get_by_name(address_name) updates = {'interface_id': interface['id']} pecan.request.dbapi.address_update(address.uuid, updates)
def post(self, infra): """Create a new infrastructure network config.""" if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: msg = _("Adding an infrastructure network on a simplex system " "is not allowed.") raise wsme.exc.ClientSideError(msg) self._check_host_states() infra_data = self._check_infra_data(infra.as_dict()) infra = self._create_infra_network(infra_data) return InfraNetwork.convert_with_links(infra)
def _create_oam_network_address(self, pool): addresses = {} if pool.floating_address: addresses.update( {constants.CONTROLLER_HOSTNAME: pool.floating_address}) if utils.get_system_mode() != constants.SYSTEM_MODE_SIMPLEX: if pool.controller0_address: addresses.update({ constants.CONTROLLER_0_HOSTNAME: pool.controller0_address }) if pool.controller1_address: addresses.update({ constants.CONTROLLER_1_HOSTNAME: pool.controller1_address }) if pool.gateway_address: addresses.update( {constants.CONTROLLER_GATEWAY: pool.gateway_address}) return addresses
def import_load_metadata(self, load): """Import a new load using only the metadata. Only available to SX subcoulds.""" LOG.info("Load import metadata request received.") err_msg = None # Enforce system type restrictions err_msg = _( "Metadata load import is only available to simplex subclouds.") if utils.get_system_mode() != constants.SYSTEM_MODE_SIMPLEX: raise wsme.exc.ClientSideError(err_msg) if utils.get_distributed_cloud_role( ) != constants.DISTRIBUTED_CLOUD_ROLE_SUBCLOUD: raise wsme.exc.ClientSideError(err_msg) self._check_existing_loads() if load.software_version == load.compatible_version: raise wsme.exc.ClientSideError(_("Invalid load software_version.")) if load.compatible_version != tsc.SW_VERSION: raise wsme.exc.ClientSideError( _("Load compatible_version does not match SW_VERSION.")) patch = load.as_dict() self._new_load_semantic_checks(patch) patch['state'] = constants.IMPORTED_METADATA_LOAD_STATE patch['uuid'] = None LOG.info("Load import metadata validated, creating new load: %s" % patch) try: new_load = pecan.request.dbapi.load_create(patch) except exception.SysinvException: LOG.exception("Failure to create load") raise wsme.exc.ClientSideError(_("Failure to create load")) return load.convert_with_links(new_load)
def post(self, body): """Create a new Software Upgrade instance and start upgrade.""" # Only start the upgrade from controller-0 if socket.gethostname() != constants.CONTROLLER_0_HOSTNAME: raise wsme.exc.ClientSideError( _("upgrade-start rejected: An upgrade can only be started " "when %s is active." % constants.CONTROLLER_0_HOSTNAME)) # There must not already be an upgrade in progress try: pecan.request.dbapi.software_upgrade_get_one() except exception.NotFound: pass else: raise wsme.exc.ClientSideError( _("upgrade-start rejected: An upgrade is already in progress.") ) # Determine the from_load and to_load loads = pecan.request.dbapi.load_get_list() from_load = cutils.get_active_load(loads) from_version = from_load.software_version to_load = cutils.get_imported_load(loads) to_version = to_load.software_version controller_0 = pecan.request.dbapi.ihost_get_by_hostname( constants.CONTROLLER_0_HOSTNAME) force = body.get('force', False) is True try: # Set the upgrade flag in VIM # This prevents VM changes during the upgrade and health checks if utils.get_system_mode() != constants.SYSTEM_MODE_SIMPLEX: vim_api.set_vim_upgrade_state(controller_0, True) except Exception as e: LOG.exception(e) raise wsme.exc.ClientSideError( _("upgrade-start rejected: Unable to set VIM upgrade state")) success, output = pecan.request.rpcapi.get_system_health( pecan.request.context, force=force, upgrade=True) if not success: LOG.info("Health audit failure during upgrade start. Health " "query results: %s" % output) if os.path.exists(constants.SYSINV_RUNNING_IN_LAB) and force: LOG.info("Running in lab, ignoring health errors.") else: vim_api.set_vim_upgrade_state(controller_0, False) raise wsme.exc.ClientSideError( _("upgrade-start rejected: System is not in a valid state " "for upgrades. Run system health-query-upgrade for more " "details.")) # Create upgrade record. Must do this before the prepare_upgrade so # the upgrade record exists when the database is dumped. create_values = { 'from_load': from_load.id, 'to_load': to_load.id, 'state': constants.UPGRADE_STARTING } new_upgrade = None try: new_upgrade = pecan.request.dbapi.software_upgrade_create( create_values) except Exception as ex: vim_api.set_vim_upgrade_state(controller_0, False) LOG.exception(ex) raise # Prepare for upgrade LOG.info("Starting upgrade from release: %s to release: %s" % (from_version, to_version)) try: pecan.request.rpcapi.start_upgrade(pecan.request.context, new_upgrade) except Exception as ex: vim_api.set_vim_upgrade_state(controller_0, False) pecan.request.dbapi.software_upgrade_destroy(new_upgrade.uuid) LOG.exception(ex) raise return Upgrade.convert_with_links(new_upgrade)
def _check_extoam_data(extoam_orig, extoam, region_config=False): subnetkey = 'oam_subnet' if subnetkey in extoam.keys(): subnet = extoam[subnetkey] try: subnet = IPNetwork(subnet) except AddrFormatError: raise wsme.exc.ClientSideError(_( "Invalid subnet %s %s." "Please configure a valid subnet" ) % (subnetkey, subnet)) try: utils.is_valid_subnet(subnet) except Exception: raise wsme.exc.ClientSideError(_( "Invalid subnet %s %s." "Please check and configure a valid OAM Subnet." ) % (subnetkey, subnet)) skip_oam_gateway_ip_check = False gateway_ipkey = 'oam_gateway_ip' gateway_ip = extoam.get(gateway_ipkey) or "" if gateway_ipkey in extoam.keys(): ogateway_ip = extoam_orig.get(gateway_ipkey) or "" osubnet = extoam_orig.get(subnetkey) or "" if not ogateway_ip and osubnet: if gateway_ip: raise wsme.exc.ClientSideError(_( "OAM gateway IP is not allowed to be configured %s %s. " "There is already a management gateway address configured." ) % (ogateway_ip, gateway_ip)) else: skip_oam_gateway_ip_check = True for k, v in extoam.items(): if k in extoam_ip_address_keys: if skip_oam_gateway_ip_check: if k == "oam_gateway_ip": continue if utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX: if k == "oam_c0_ip" or k == 'oam_c1_ip': continue try: v = IPAddress(v) except (AddrFormatError, ValueError): raise wsme.exc.ClientSideError(_( "Invalid address %s in %s." " Please configure a valid" " IPv%s address" ) % (v, k, str(subnet.version))) utils.is_valid_address_within_subnet(v, subnet) oam_c0_ip = extoam.get('oam_c0_ip') or "" oam_c1_ip = extoam.get('oam_c1_ip') or "" # check for unique if not empty if oam_c0_ip and oam_c0_ip == oam_c1_ip: raise wsme.exc.ClientSideError(_( "Invalid address: " "oam_c0_ip=%s and oam_c1_ip=%s must be unique. " ) % (oam_c0_ip, oam_c1_ip)) if gateway_ip and (gateway_ip == oam_c0_ip) or (gateway_ip == oam_c1_ip): raise wsme.exc.ClientSideError(_( "Invalid address: " "oam_c0_ip=%s, oam_c1_ip=%s, oam_gateway_ip=%s must be unique." ) % (oam_c0_ip, oam_c1_ip, gateway_ip)) # Region Mode, check if addresses are within start and end range # Gateway address is not used in region mode subnet = IPNetwork(extoam.get('oam_subnet')) floating_address = IPAddress(extoam.get('oam_floating_ip')) start_address = IPAddress(extoam.get('oam_start_ip')) end_address = IPAddress(extoam.get('oam_end_ip')) # check whether start and end addresses are within the oam_subnet range if start_address not in subnet: if region_config: raise wsme.exc.ClientSideError(_( "Invalid oam_start_ip=%s. Please configure a valid IP address") % start_address) LOG.info("Updating oam_start_ip=%s to %s" % (start_address, subnet[1])) extoam['oam_start_ip'] = subnet[1] start_address = IPAddress(extoam.get('oam_start_ip')) if end_address not in subnet: if region_config: raise wsme.exc.ClientSideError(_( "Invalid oam_end_ip=%s. Please configure a valid IP address") % end_address) LOG.info("Updating oam_end_ip=%s to %s" % (end_address, subnet[-2])) extoam['oam_end_ip'] = subnet[-2] end_address = IPAddress(extoam.get('oam_end_ip')) if floating_address not in IPRange(start_address, end_address): raise wsme.exc.ClientSideError(_( "Invalid oam_floating_ip=%s. Please configure a valid IP address " "in range") % floating_address) if oam_c0_ip and IPAddress(oam_c0_ip) not in IPRange(start_address, end_address): raise wsme.exc.ClientSideError(_( "Invalid oam_c0_ip=%s. Please configure a valid IP address " "in range") % oam_c0_ip) if oam_c1_ip and IPAddress(oam_c1_ip) not in IPRange(start_address, end_address): raise wsme.exc.ClientSideError(_( "Invalid oam_c1_ip=%s. Please configure a valid IP address " "in range") % oam_c1_ip) return extoam
def _semantic_checks(operation, partition): # Semantic checks LOG.debug("PART Partition semantic checks for %s operation" % operation) ihost = pecan.request.dbapi.ihost_get(partition['forihostid']) # Get disk. idiskid = partition.get('idisk_id') or partition.get('idisk_uuid') idisk = pecan.request.dbapi.idisk_get(idiskid) # Check host and host state. _check_host(partition, ihost, idisk) # Make sure this partition's type is valid. _check_partition_type(partition) # Check existing partitions and make sure we don't have any partitions # being changed for an existing host/disk pairing. If so => reject request. _check_for_outstanding_requests(partition, idisk) # Make sure the disk on which we create the partition is valid. _check_disk(idisk) # Semantic checks based on operation. if operation == constants.PARTITION_CMD_CREATE: ############ # CREATING # ############ if int(partition['size_mib']) <= 0: raise wsme.exc.ClientSideError( _("Partition size must be greater than 0.")) # Check if there is enough space on the disk to accommodate the # partition. if not _enough_avail_space_on_disk(partition.get('size_mib'), idisk): raise wsme.exc.ClientSideError( _("Requested size %s GiB is larger than the %s GiB " "available.") % (partition['size_mib'] / 1024, math.floor(float(idisk.available_mib) / 1024 * 1000) / 1000.0)) _are_partition_operations_simultaneous(ihost, partition, constants.PARTITION_CMD_CREATE) # Enough space is availabe, save the disk ID. if uuidutils.is_uuid_like(idiskid): idisk_id = idisk['id'] else: idisk_id = idiskid partition.update({'idisk_id': idisk_id}) elif operation == constants.PARTITION_CMD_MODIFY: ############# # MODIFYING # ############# # Only allow in-service modify of partitions. If the host isn't # provisioned just limit operations to create/delete. if ihost.invprovision != constants.PROVISIONED: raise wsme.exc.ClientSideError( _("Only partition Add/Delete operations are allowed on an " "unprovisioned host.")) # Allow modification of in-use PVs only for cinder-volumes ipv_uuid = partition.get('ipv_uuid') ipv_lvg_name = None if ipv_uuid: ipv_lvg_name = pecan.request.dbapi.ipv_get(ipv_uuid)['lvm_vg_name'] if (ipv_lvg_name != constants.LVG_CINDER_VOLUMES and (ipv_uuid or partition.get('status') == constants.PARTITION_IN_USE_STATUS)): raise wsme.exc.ClientSideError( _("Can not modify partition. A physical volume (%s) is " "currently associated with this partition.") % partition.get('device_node')) if (ipv_lvg_name == constants.LVG_CINDER_VOLUMES): if (utils.get_system_mode() == constants.SYSTEM_MODE_SIMPLEX): if ihost['administrative'] != constants.ADMIN_LOCKED: raise wsme.exc.ClientSideError( _("Cannot modify the partition (%(dev_node)s) associated with " "the physical volume (%(PV)s) while the host is unlocked.") % {'dev_node': partition.get('device_node'), 'PV': ipv_uuid}) # TODO(oponcea) Deny modifications if instances are still running. elif utils.is_host_active_controller(ihost): raise wsme.exc.ClientSideError( _("Can only modify the partition (%(dev_node)s) associated with the physical " "volume (%(PV)s) if the personality is 'Controller-Standby'") % {'dev_node': partition.get('device_node'), 'PV': ipv_uuid}) # Prevent modifying a partition that is in creating state. allowed_states = [constants.PARTITION_READY_STATUS] if ipv_lvg_name == constants.LVG_CINDER_VOLUMES: allowed_states.append(constants.PARTITION_IN_USE_STATUS) status = partition.get('status') if status not in allowed_states: raise wsme.exc.ClientSideError( _("Can not modify partition. Only partitions in the %s state " "can be modified.") % constants.PARTITION_STATUS_MSG[ constants.PARTITION_READY_STATUS]) # Check that the partition to modify is the last partition. if not cutils.is_partition_the_last(pecan.request.dbapi, partition): raise wsme.exc.ClientSideError( _("Can not modify partition. Only the last partition on disk " "can be modified.")) # Obtain the current partition info. crt_part = pecan.request.dbapi.partition_get(partition.get('uuid')) crt_part_size = crt_part.size_mib new_part_size = partition.get('size_mib') extra_size = new_part_size - crt_part_size # Check if there is enough space to enlarge the partition. if not _enough_avail_space_on_disk(extra_size, idisk): raise wsme.exc.ClientSideError( _("Requested extra size %s GiB is larger than the %s GiB " "available.") % (extra_size / 1024, math.floor(float(idisk.available_mib) / 1024 * 1000) / 1000.0)) elif operation == constants.PARTITION_CMD_DELETE: ############ # DELETING # ############ # Make sure that there is no PV associated with this partition if (partition.get('ipv_uuid') or partition.get('status') == constants.PARTITION_IN_USE_STATUS): raise wsme.exc.ClientSideError( _("Can not delete partition. A physical volume (%s) is " "currently associated with this partition") % partition.get('device_node')) _are_partition_operations_simultaneous(ihost, partition, constants.PARTITION_CMD_DELETE) status = partition.get('status') if status == constants.PARTITION_READY_STATUS: # Check that the partition to delete is the last partition. if not cutils.is_partition_the_last(pecan.request.dbapi, partition): raise wsme.exc.ClientSideError( _("Can not delete partition. Only the last partition on " "disk can be deleted.")) elif status not in constants.PARTITION_STATUS_OK_TO_DELETE: raise wsme.exc.ClientSideError( _("Can not delete partition. Only partitions in one of these " "states can be deleted: %s") % ", ".join( map(constants.PARTITION_STATUS_MSG.get, constants.PARTITION_STATUS_OK_TO_DELETE))) else: raise wsme.exc.ClientSideError( _("Internal Error: Invalid Partition operation: %s" % operation)) return partition