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)
Exemple #2
0
    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)
Exemple #3
0
    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
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
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