Example #1
0
 def _format_server_group(self, context, group, req):
     # the id field has its value as the uuid of the server group
     # There is no 'uuid' key in server_group seen by clients.
     # In addition, clients see policies as a ["policy-name"] list;
     # and they see members as a ["server-id"] list.
     server_group = {}
     server_group['id'] = group.uuid
     server_group['name'] = group.name
     if api_version_request.is_supported(
             req, min_version=GROUP_POLICY_OBJ_MICROVERSION):
         server_group['policy'] = group.policy
         server_group['rules'] = group.rules
     else:
         server_group['policies'] = group.policies or []
         # NOTE(yikun): Before v2.64, a empty metadata is exposed to the
         # user, and it is removed since v2.64.
         server_group['metadata'] = {}
     members = []
     if group.members:
         # Display the instances that are not deleted.
         members = _get_not_deleted(context, group.members)
     server_group['members'] = members
     # Add project id information to the response data for
     # API version v2.13
     if api_version_request.is_supported(req, min_version="2.13"):
         server_group['project_id'] = group.project_id
         server_group['user_id'] = group.user_id
     return server_group
Example #2
0
 def _get_server_search_options(self, req):
     """Return server search options allowed by non-admin."""
     opt_list = ('reservation_id', 'name', 'status', 'image', 'flavor',
                 'ip', 'changes-since', 'all_tenants')
     if api_version_request.is_supported(req, min_version='2.5'):
         opt_list += ('ip6',)
     if api_version_request.is_supported(req, min_version='2.26'):
         opt_list += TAG_SEARCH_FILTERS
     return opt_list
Example #3
0
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        context.can(ms_policies.POLICY_ROOT % 'migrate_live')

        host = body["os-migrateLive"]["host"]
        block_migration = body["os-migrateLive"]["block_migration"]
        force = None
        async_ = api_version_request.is_supported(req, min_version='2.34')
        if api_version_request.is_supported(req, min_version='2.30'):
            force = self._get_force_param_for_live_migration(body, host)
        if api_version_request.is_supported(req, min_version='2.25'):
            if block_migration == 'auto':
                block_migration = None
            else:
                block_migration = strutils.bool_from_string(block_migration,
                                                            strict=True)
            disk_over_commit = None
        else:
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]

            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host, force,
                                          async_)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.NoValidHost,
                exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError) as ex:
            if async_:
                with excutils.save_and_reraise_exception():
                    LOG.error("Unexpected exception received from "
                              "conductor during pre-live-migration checks "
                              "'%(ex)s'", {'ex': ex})
            else:
                raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.InstanceIsLocked as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.ComputeHostNotFound as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'os-migrateLive', id)
    def test_is_supported_max_version(self):
        req = fakes.HTTPRequest.blank('/fake', version='2.5')

        self.assertFalse(api_version_request.is_supported(
            req, max_version='2.4'))
        self.assertTrue(api_version_request.is_supported(
            req, max_version='2.5'))
        self.assertTrue(api_version_request.is_supported(
            req, max_version='2.6'))
Example #5
0
    def _evacuate(self, req, id, body):
        """Permit admins to evacuate a server from a failed host
        to a new one.
        """
        context = req.environ["nova.context"]
        instance = common.get_instance(self.compute_api, context, id)
        context.can(evac_policies.BASE_POLICY_NAME,
                    target={'user_id': instance.user_id,
                            'project_id': instance.project_id})

        evacuate_body = body["evacuate"]
        host = evacuate_body.get("host")
        force = None

        on_shared_storage = self._get_on_shared_storage(req, evacuate_body)

        if api_version_request.is_supported(req, min_version='2.29'):
            force = body["evacuate"].get("force", False)
            force = strutils.bool_from_string(force, strict=True)
            if force is True and not host:
                message = _("Can't force to a non-provided destination")
                raise exc.HTTPBadRequest(explanation=message)
        if api_version_request.is_supported(req, min_version='2.14'):
            password = self._get_password_v214(req, evacuate_body)
        else:
            password = self._get_password(req, evacuate_body,
                                          on_shared_storage)

        if host is not None:
            try:
                self.host_api.service_get_by_compute_host(context, host)
            except (exception.ComputeHostNotFound,
                    exception.HostMappingNotFound):
                msg = _("Compute host %s not found.") % host
                raise exc.HTTPNotFound(explanation=msg)

        if instance.host == host:
            msg = _("The target host can't be the same one.")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            self.compute_api.evacuate(context, instance, host,
                                      on_shared_storage, password, force)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'evacuate', id)
        except exception.ComputeServiceInUse as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if (not api_version_request.is_supported(req, min_version='2.14') and
                CONF.api.enable_instance_password):
            return {'adminPass': password}
        else:
            return None
Example #6
0
    def _create_backup(self, req, id, body):
        """Backup a server instance.

        Images now have an `image_type` associated with them, which can be
        'snapshot' or the backup type, like 'daily' or 'weekly'.

        If the image_type is backup-like, then the rotation factor can be
        included and that will cause the oldest backups that exceed the
        rotation factor to be deleted.

        """
        context = req.environ["nova.context"]
        context.can(cb_policies.BASE_POLICY_NAME)
        entity = body["createBackup"]

        image_name = common.normalize_name(entity["name"])
        backup_type = entity["backup_type"]
        rotation = int(entity["rotation"])

        props = {}
        metadata = entity.get('metadata', {})
        # Starting from microversion 2.39 we don't check quotas on createBackup
        if api_version_request.is_supported(
                req, max_version=
                api_version_request.MAX_IMAGE_META_PROXY_API_VERSION):
            common.check_img_metadata_properties_quota(context, metadata)
        props.update(metadata)

        instance = common.get_instance(self.compute_api, context, id)

        try:
            image = self.compute_api.backup(context, instance, image_name,
                    backup_type, rotation, extra_properties=props)
        except exception.InstanceUnknownCell as e:
            raise webob.exc.HTTPNotFound(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'createBackup', id)
        except exception.InvalidRequest as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        # Starting with microversion 2.45 we return a response body containing
        # the snapshot image id without the Location header.
        if api_version_request.is_supported(req, '2.45'):
            return {'image_id': image['id']}

        resp = webob.Response(status_int=202)

        # build location of newly-created image entity if rotation is not zero
        if rotation > 0:
            image_id = str(image['id'])
            image_ref = common.url_join(req.application_url, 'images',
                                        image_id)
            resp.headers['Location'] = image_ref

        return resp
Example #7
0
    def _create(self, req, body):
        context = req.environ['nova.context']
        # TODO(rb560u): remove this check in future release
        using_old_action = \
            policy.verify_deprecated_policy(fm_policies.BASE_POLICY_NAME,
                fm_policies.POLICY_ROOT % 'create',
                base.RULE_ADMIN_API,
                context)

        if not using_old_action:
            context.can(fm_policies.POLICY_ROOT % 'create')

        vals = body['flavor']

        name = vals['name']
        flavorid = vals.get('id')
        memory = vals['ram']
        vcpus = vals['vcpus']
        root_gb = vals['disk']
        ephemeral_gb = vals.get('OS-FLV-EXT-DATA:ephemeral', 0)
        swap = vals.get('swap', 0)
        rxtx_factor = vals.get('rxtx_factor', 1.0)
        is_public = vals.get('os-flavor-access:is_public', True)

        # The user can specify a description starting with microversion 2.55.
        include_description = api_version_request.is_supported(
            req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
        description = vals.get('description') if include_description else None

        try:
            flavor = flavors.create(name, memory, vcpus, root_gb,
                                    ephemeral_gb=ephemeral_gb,
                                    flavorid=flavorid, swap=swap,
                                    rxtx_factor=rxtx_factor,
                                    is_public=is_public,
                                    description=description)
            # NOTE(gmann): For backward compatibility, non public flavor
            # access is not being added for created tenant. Ref -bug/1209101
            req.cache_db_flavor(flavor)
        except (exception.FlavorExists,
                exception.FlavorIdExists) as err:
            raise webob.exc.HTTPConflict(explanation=err.format_message())

        include_extra_specs = False
        if api_version_request.is_supported(
                req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION):
            include_extra_specs = context.can(
                fes_policies.POLICY_ROOT % 'index', fatal=False)
            # NOTE(yikun): This empty extra_spec only for keeping consistent
            # with other related flavor api.
            flavor.extra_specs = {}

        return self._view_builder.show(req, flavor, include_description,
                                       include_extra_specs=include_extra_specs)
Example #8
0
    def show(self, req, server_id, id):
        """Return data about the given instance action."""
        context = req.environ['nova.context']
        instance = self._get_instance(req, context, server_id)
        context.can(ia_policies.BASE_POLICY_NAME, instance)
        action = self.action_api.action_get_by_request_id(context, instance,
                                                          id)
        if action is None:
            msg = _("Action %s not found") % id
            raise exc.HTTPNotFound(explanation=msg)

        action_id = action['id']
        if api_version_request.is_supported(req, min_version="2.58"):
            action = self._format_action(action, ACTION_KEYS_V258)
        else:
            action = self._format_action(action, ACTION_KEYS)
        # Prior to microversion 2.51, events would only be returned in the
        # response for admins by default policy rules. Starting in
        # microversion 2.51, events are returned for admin_or_owner (of the
        # instance) but the "traceback" field is only shown for admin users
        # by default.
        show_events = False
        show_traceback = False
        show_host = False
        if context.can(ia_policies.POLICY_ROOT % 'events', fatal=False):
            # For all microversions, the user can see all event details
            # including the traceback.
            show_events = show_traceback = True
            show_host = api_version_request.is_supported(req, '2.62')
        elif api_version_request.is_supported(req, '2.51'):
            # The user is not able to see all event details, but they can at
            # least see the non-traceback event details.
            show_events = True

        # An obfuscated hashed host id is returned since microversion 2.62
        # for all users.
        show_hostid = api_version_request.is_supported(req, '2.62')

        if show_events:
            events_raw = self.action_api.action_events_get(context, instance,
                                                           action_id)
            # NOTE(takashin): The project IDs of instance action events
            # become null (None) when instance action events are created
            # by periodic tasks. If the project ID is null (None),
            # it causes an error when 'hostId' is generated.
            # If the project ID is null (None), pass the project ID of
            # the server instead of that of instance action events.
            action['events'] = [self._format_event(
                evt, action['project_id'] or instance.project_id,
                show_traceback=show_traceback,
                show_host=show_host, show_hostid=show_hostid
            ) for evt in events_raw]
        return {'instanceAction': action}
Example #9
0
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        authorize(context, action='migrate_live')

        host = body["os-migrateLive"]["host"]
        block_migration = body["os-migrateLive"]["block_migration"]
        force = None

        if api_version_request.is_supported(req, min_version='2.30'):
            force = body["os-migrateLive"].get("force", False)
            force = strutils.bool_from_string(force, strict=True)
            if force is True and not host:
                message = _("Can't force to a non-provided destination")
                raise exc.HTTPBadRequest(explanation=message)
        if api_version_request.is_supported(req, min_version='2.25'):
            if block_migration == 'auto':
                block_migration = None
            else:
                block_migration = strutils.bool_from_string(block_migration,
                                                            strict=True)
            disk_over_commit = None
        else:
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]

            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)

        try:
            instance = common.get_instance(self.compute_api, context, id)
            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host, force)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.NoValidHost,
                exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError,
                exception.LiveMigrationWithOldNovaNotSupported) as ex:
            raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.InstanceIsLocked as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'os-migrateLive', id)
Example #10
0
    def show(self, request, instance, extend_address=True):
        """Detailed view of a single instance."""
        ip_v4 = instance.get('access_ip_v4')
        ip_v6 = instance.get('access_ip_v6')

        server = {
            "server": {
                "id": instance["uuid"],
                "name": instance["display_name"],
                "status": self._get_vm_status(instance),
                "tenant_id": instance.get("project_id") or "",
                "user_id": instance.get("user_id") or "",
                "metadata": self._get_metadata(instance),
                "hostId": self._get_host_id(instance) or "",
                "image": self._get_image(request, instance),
                "flavor": self._get_flavor(request, instance),
                "created": utils.isotime(instance["created_at"]),
                "updated": utils.isotime(instance["updated_at"]),
                "addresses": self._get_addresses(request, instance,
                                                 extend_address),
                "accessIPv4": str(ip_v4) if ip_v4 is not None else '',
                "accessIPv6": str(ip_v6) if ip_v6 is not None else '',
                "links": self._get_links(request,
                                         instance["uuid"],
                                         self._collection_name),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig": (
                    'AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True if instance["locked_by"]
                                          else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                                                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        return server
Example #11
0
    def show(self, req, server_id, id):
        """Return data about the given instance action."""
        context = req.environ['nova.context']
        instance = self._get_instance(req, context, server_id)
        context.can(ia_policies.BASE_POLICY_NAME, instance)
        action = self.action_api.action_get_by_request_id(context, instance,
                                                          id)
        if action is None:
            msg = _("Action %s not found") % id
            raise exc.HTTPNotFound(explanation=msg)

        action_id = action['id']
        action = self._format_action(action)
        # Prior to microversion 2.51, events would only be returned in the
        # response for admins by default policy rules. Starting in
        # microversion 2.51, events are returned for admin_or_owner (of the
        # instance) but the "traceback" field is only shown for admin users
        # by default.
        show_events = False
        show_traceback = False
        if context.can(ia_policies.POLICY_ROOT % 'events', fatal=False):
            # For all microversions, the user can see all event details
            # including the traceback.
            show_events = show_traceback = True
        elif api_version_request.is_supported(req, '2.51'):
            # The user is not able to see all event details, but they can at
            # least see the non-traceback event details.
            show_events = True

        if show_events:
            events_raw = self.action_api.action_events_get(context, instance,
                                                           action_id)
            action['events'] = [self._format_event(evt, show_traceback)
                                for evt in events_raw]
        return {'instanceAction': action}
Example #12
0
 def index(self, request, flavors):
     """Return the 'index' view of flavors."""
     coll_name = self._collection_name
     include_description = api_version_request.is_supported(
         request, FLAVOR_DESCRIPTION_MICROVERSION)
     return self._list_view(self.basic, request, flavors, coll_name,
                            include_description=include_description)
Example #13
0
    def _validate_id(req, hypervisor_id):
        """Validates that the id is a uuid for microversions that require it.

        :param req: The HTTP request object which contains the requested
            microversion information.
        :param hypervisor_id: The provided hypervisor id.
        :raises: webob.exc.HTTPBadRequest if the requested microversion is
            greater than or equal to 2.53 and the id is not a uuid.
        :raises: webob.exc.HTTPNotFound if the requested microversion is
            less than 2.53 and the id is not an integer.
        """
        expect_uuid = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)
        if expect_uuid:
            if not uuidutils.is_uuid_like(hypervisor_id):
                msg = _('Invalid uuid %s') % hypervisor_id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            # This API is supported for cells v1 and as such the id can be
            # a cell v1 delimited string, so we have to parse it first.
            if cells_utils.CELL_ITEM_SEP in str(hypervisor_id):
                hypervisor_id = cells_utils.split_cell_and_item(
                    hypervisor_id)[1]
            try:
                utils.validate_integer(hypervisor_id, 'id')
            except exception.InvalidInput:
                msg = (_("Hypervisor with ID '%s' could not be found.") %
                       hypervisor_id)
                raise webob.exc.HTTPNotFound(explanation=msg)
Example #14
0
    def show(self, req, server_id, id):
        """Return the migration of an instance in progress by id."""
        context = req.environ['nova.context']
        context.can(sm_policies.POLICY_ROOT % 'show')

        # NOTE(Shaohe Feng) just check the instance is available. To keep
        # consistency with other API, check it before get migrations.
        common.get_instance(self.compute_api, context, server_id)

        try:
            migration = self.compute_api.get_migration_by_id_and_instance(
                    context, id, server_id)
        except exception.MigrationNotFoundForInstance:
            msg = _("In-progress live migration %(id)s is not found for"
                    " server %(uuid)s.") % {"id": id, "uuid": server_id}
            raise exc.HTTPNotFound(explanation=msg)

        if migration.get("migration_type") != "live-migration":
            msg = _("Migration %(id)s for server %(uuid)s is not"
                    " live-migration.") % {"id": id, "uuid": server_id}
            raise exc.HTTPNotFound(explanation=msg)

        # TODO(Shaohe Feng) we should share the in-progress list.
        in_progress = ['queued', 'preparing', 'running', 'post-migrating']
        if migration.get("status") not in in_progress:
            msg = _("Live migration %(id)s for server %(uuid)s is not in"
                    " progress.") % {"id": id, "uuid": server_id}
            raise exc.HTTPNotFound(explanation=msg)

        include_uuid = api_version_request.is_supported(req, '2.59')
        return {'migration': output(migration, include_uuid)}
Example #15
0
    def _index(self, req, user_id=None, links=False, **keypair_filters):
        """List of keypairs for a user."""
        context = req.environ['nova.context']
        user_id = user_id or context.user_id
        context.can(kp_policies.POLICY_ROOT % 'index',
                    target={'user_id': user_id,
                            'project_id': context.project_id})

        if api_version_request.is_supported(req, min_version='2.35'):
            limit, marker = common.get_limit_and_marker(req)
        else:
            limit = marker = None

        try:
            key_pairs = self.api.get_key_pairs(
                context, user_id, limit=limit, marker=marker)
        except exception.MarkerNotFound as e:
            raise webob.exc.HTTPBadRequest(explanation=e.format_message())

        key_pairs = [self._filter_keypair(key_pair, **keypair_filters)
                     for key_pair in key_pairs]

        keypairs_list = [{'keypair': key_pair} for key_pair in key_pairs]
        keypairs_dict = {'keypairs': keypairs_list}

        if links:
            keypairs_links = self._view_builder.get_links(req, key_pairs)

            if keypairs_links:
                keypairs_dict['keypairs_links'] = keypairs_links

        return keypairs_dict
 def detail(self, req, resp_obj):
     context = req.environ['nova.context']
     authorize_extend = False
     authorize_host_status = False
     if context.can(esa_policies.BASE_POLICY_NAME, fatal=False):
         authorize_extend = True
     if (api_version_request.is_supported(req, min_version='2.16') and
         context.can(servers_policies.SERVERS % 'show:host_status',
                     fatal=False)):
         authorize_host_status = True
     if authorize_extend or authorize_host_status:
         servers = list(resp_obj.obj['servers'])
         # NOTE(dinesh-bhor): Skipping fetching of instances from cache as
         # servers list can be empty if invalid status is provided to the
         # core API 'detail' method.
         if servers:
             instances = req.get_db_instances()
             if authorize_host_status:
                 host_statuses = (
                     self.compute_api.get_instances_host_statuses(
                             instances.values()))
             for server in servers:
                 if authorize_extend:
                     instance = instances[server['id']]
                     self._extend_server(context, server, instance, req)
                 if authorize_host_status:
                     server['host_status'] = host_statuses[server['id']]
Example #17
0
 def _format_server_group(self, context, group, req):
     # the id field has its value as the uuid of the server group
     # There is no 'uuid' key in server_group seen by clients.
     # In addition, clients see policies as a ["policy-name"] list;
     # and they see members as a ["server-id"] list.
     server_group = {}
     server_group['id'] = group.uuid
     server_group['name'] = group.name
     server_group['policies'] = group.policies or []
     # NOTE(danms): This has been exposed to the user, but never used.
     # Since we can't remove it, just make sure it's always empty.
     server_group['metadata'] = {}
     members = []
     if group.members:
         # Display the instances that are not deleted.
         filters = {'uuid': group.members, 'deleted': False}
         instances = objects.InstanceList.get_by_filters(
             context, filters=filters)
         members = [instance.uuid for instance in instances]
     server_group['members'] = members
     # Add project id information to the response data for
     # API version v2.13
     if api_version_request.is_supported(req, min_version="2.13"):
         server_group['project_id'] = group.project_id
         server_group['user_id'] = group.user_id
     return server_group
Example #18
0
    def _get_services(self, req):
        # The API services are filtered out since they are not RPC services
        # and therefore their state is not reported through the service group
        # API, so they would always be reported as 'down' (see bug 1543625).
        api_services = ('nova-osapi_compute', 'nova-metadata')

        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        cell_down_support = api_version_request.is_supported(
            req, min_version=PARTIAL_CONSTRUCT_FOR_CELL_DOWN_MIN_VERSION)

        _services = [
            s
            for s in self.host_api.service_get_all(context, set_zones=True,
                all_cells=True, cell_down_support=cell_down_support)
            if s['binary'] not in api_services
        ]

        host = ''
        if 'host' in req.GET:
            host = req.GET['host']
        binary = ''
        if 'binary' in req.GET:
            binary = req.GET['binary']
        if host:
            _services = [s for s in _services if s['host'] == host]
        if binary:
            _services = [s for s in _services if s['binary'] == binary]

        return _services
Example #19
0
    def index(self, req, resp_obj):
        context = req.environ['nova.context']
        project_id = self._project_id(context, req)
        quotas = QUOTAS.get_project_quotas(context, project_id, usages=True)
        if api_version_request.is_supported(
                req, min_version=MIN_WITHOUT_PROXY_API_SUPPORT_VERSION):
            quota_map = {
                'totalRAMUsed': 'ram',
                'totalCoresUsed': 'cores',
                'totalInstancesUsed': 'instances',
                'totalServerGroupsUsed': 'server_groups',
            }
        else:
            quota_map = {
                'totalRAMUsed': 'ram',
                'totalCoresUsed': 'cores',
                'totalInstancesUsed': 'instances',
                'totalFloatingIpsUsed': 'floating_ips',
                'totalSecurityGroupsUsed': 'security_groups',
                'totalServerGroupsUsed': 'server_groups',
            }

        used_limits = {}
        for display_name, key in quota_map.items():
            if key in quotas:
                reserved = (quotas[key]['reserved']
                            if self._reserved(req) else 0)
                used_limits[display_name] = quotas[key]['in_use'] + reserved

        resp_obj.obj['limits']['absolute'].update(used_limits)
    def _extend_server(self, context, server, instance, req):
        key = "OS-EXT-SRV-ATTR:hypervisor_hostname"
        server[key] = instance.node

        properties = ["host", "name"]
        if api_version_request.is_supported(req, min_version="2.3"):
            # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix below
            # and that's OK for microversion 2.3 which is being compatible
            # with v2.0 for the ec2 API split out from Nova. After this,
            # however, new microversoins should not be using the
            # OS-EXT-SRV-ATTR prefix.
            properties += [
                "reservation_id",
                "launch_index",
                "hostname",
                "kernel_id",
                "ramdisk_id",
                "root_device_name",
                "user_data",
            ]
        for attr in properties:
            if attr == "name":
                key = "OS-EXT-SRV-ATTR:instance_%s" % attr
            else:
                # NOTE(mriedem): Nothing after microversion 2.3 should use the
                # OS-EXT-SRV-ATTR prefix for the attribute key name.
                key = "OS-EXT-SRV-ATTR:%s" % attr
            server[key] = instance[attr]
Example #21
0
    def _migrate(self, req, id, body):
        """Permit admins to migrate a server to a new host."""
        context = req.environ['nova.context']
        context.can(ms_policies.POLICY_ROOT % 'migrate')

        host_name = None
        if (api_version_request.is_supported(req, min_version='2.56') and
            body['migrate'] is not None):
            host_name = body['migrate'].get('host')

        instance = common.get_instance(self.compute_api, context, id)
        try:
            self.compute_api.resize(req.environ['nova.context'], instance,
                                    host_name=host_name)
        except (exception.TooManyInstances, exception.QuotaError) as e:
            raise exc.HTTPForbidden(explanation=e.format_message())
        except (exception.InstanceIsLocked,
                exception.CannotMigrateWithTargetHost) as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'migrate', id)
        except exception.InstanceNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.NoValidHost, exception.ComputeHostNotFound,
                exception.CannotMigrateToSameHost) as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
Example #22
0
    def index(self, req, server_id):
        context = req.environ["nova.context"]
        context.can(sd_policies.BASE_POLICY_NAME)

        instance = common.get_instance(self.compute_api, context, server_id)

        try:
            if api_version_request.is_supported(req, min_version='2.48'):
                diagnostics = self.compute_api.get_instance_diagnostics(
                    context, instance)
                return self._view_builder.instance_diagnostics(diagnostics)

            return self.compute_api.get_diagnostics(context, instance)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'get_diagnostics', server_id)
        except exception.InstanceNotReady as e:
            raise webob.exc.HTTPConflict(explanation=e.format_message())
        except exception.InstanceDiagnosticsNotSupported:
            # NOTE(snikitin): During upgrade we may face situation when env
            # has new API and old compute. New compute returns a
            # Diagnostics object. Old compute returns a dictionary. So we
            # can't perform a request correctly if compute is too old.
            msg = _('Compute node is too old. You must complete the '
                    'upgrade process to be able to get standardized '
                    'diagnostics data which is available since v2.48. However '
                    'you are still able to get diagnostics data in '
                    'non-standardized format which is available until v2.47.')
            raise webob.exc.HTTPBadRequest(explanation=msg)
        except NotImplementedError:
            common.raise_feature_not_supported()
Example #23
0
    def show(self, req, server_id, id):
        """Return data about the given interface attachment."""
        context = req.environ['nova.context']
        context.can(ai_policies.BASE_POLICY_NAME)

        port_id = id
        # NOTE(mriedem): We need to verify the instance actually exists from
        # the server_id even though we're not using the instance for anything,
        # just the port id.
        common.get_instance(self.compute_api, context, server_id)

        try:
            port_info = self.network_api.show_port(context, port_id)
        except exception.PortNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.Forbidden as e:
            raise exc.HTTPForbidden(explanation=e.format_message())

        if port_info['port']['device_id'] != server_id:
            msg = _("Instance %(instance)s does not have a port with id "
                    "%(port)s") % {'instance': server_id, 'port': port_id}
            raise exc.HTTPNotFound(explanation=msg)

        return {'interfaceAttachment':
                _translate_interface_attachment_view(
                    context, port_info['port'],
                    show_tag=api_version_request.is_supported(req, '2.70'))}
Example #24
0
    def _get_service_detail(self, svc, additional_fields, req):
        alive = self.servicegroup_api.service_is_up(svc)
        state = (alive and "up") or "down"
        active = 'enabled'
        if svc['disabled']:
            active = 'disabled'
        updated_time = self.servicegroup_api.get_updated_time(svc)

        uuid_for_id = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)

        if 'availability_zone' not in svc:
            # The service wasn't loaded with the AZ so we need to do it here.
            # Yes this looks weird, but set_availability_zones makes a copy of
            # the list passed in and mutates the objects within it, so we have
            # to pull it back out from the resulting copied list.
            svc.availability_zone = (
                availability_zones.set_availability_zones(
                    req.environ['nova.context'],
                    [svc])[0]['availability_zone'])

        service_detail = {'binary': svc['binary'],
                          'host': svc['host'],
                          'id': svc['uuid' if uuid_for_id else 'id'],
                          'zone': svc['availability_zone'],
                          'status': active,
                          'state': state,
                          'updated_at': updated_time,
                          'disabled_reason': svc['disabled_reason']}

        for field in additional_fields:
            service_detail[field] = svc[field]

        return service_detail
Example #25
0
    def _evacuate(self, req, id, body):
        """Permit admins to evacuate a server from a failed host
        to a new one.
        """
        context = req.environ["nova.context"]
        authorize(context)

        evacuate_body = body["evacuate"]
        host = evacuate_body.get("host")

        on_shared_storage = self._get_on_shared_storage(req, evacuate_body)

        if api_version_request.is_supported(req, min_version='2.14'):
            password = self._get_password_v214(req, evacuate_body)
        else:
            password = self._get_password(req, evacuate_body,
                                          on_shared_storage)

        if host is not None:
            try:
                self.host_api.service_get_by_compute_host(context, host)
            except exception.ComputeHostNotFound:
                msg = _("Compute host %s not found.") % host
                raise exc.HTTPNotFound(explanation=msg)

        instance = common.get_instance(self.compute_api, context, id)
        if instance.host == host:
            msg = _("The target host can't be the same one.")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            self.compute_api.evacuate(context, instance, host,
                                      on_shared_storage, password)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'evacuate', id)
        except exception.ComputeServiceInUse as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if (not api_version_request.is_supported(req, min_version='2.14') and
                CONF.enable_instance_password):
            return {'adminPass': password}
        else:
            return None
Example #26
0
    def _view_hypervisor(self, hypervisor, service, detail, req, servers=None,
                         **kwargs):
        alive = self.servicegroup_api.service_is_up(service)
        # The 2.53 microversion returns the compute node uuid rather than id.
        uuid_for_id = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)
        hyp_dict = {
            'id': hypervisor.uuid if uuid_for_id else hypervisor.id,
            'hypervisor_hostname': hypervisor.hypervisor_hostname,
            'state': 'up' if alive else 'down',
            'status': ('disabled' if service.disabled
                       else 'enabled'),
            }

        if detail:
            for field in ('vcpus', 'memory_mb', 'local_gb', 'vcpus_used',
                          'memory_mb_used', 'local_gb_used',
                          'hypervisor_type', 'hypervisor_version',
                          'free_ram_mb', 'free_disk_gb', 'current_workload',
                          'running_vms', 'disk_available_least', 'host_ip'):
                hyp_dict[field] = getattr(hypervisor, field)

            service_id = service.uuid if uuid_for_id else service.id
            hyp_dict['service'] = {
                'id': service_id,
                'host': hypervisor.host,
                'disabled_reason': service.disabled_reason,
                }

            if api_version_request.is_supported(req, min_version='2.28'):
                if hypervisor.cpu_info:
                    hyp_dict['cpu_info'] = jsonutils.loads(hypervisor.cpu_info)
                else:
                    hyp_dict['cpu_info'] = {}
            else:
                hyp_dict['cpu_info'] = hypervisor.cpu_info

        if servers:
            hyp_dict['servers'] = [dict(name=serv['name'], uuid=serv['uuid'])
                                   for serv in servers]

        # Add any additional info
        if kwargs:
            hyp_dict.update(kwargs)

        return hyp_dict
Example #27
0
    def show(self, req, id):
        """Return data about the given flavor id."""
        context = req.environ['nova.context']
        try:
            flavor = flavors.get_flavor_by_flavor_id(id, ctxt=context)
        except exception.FlavorNotFound as e:
            raise webob.exc.HTTPNotFound(explanation=e.format_message())

        include_extra_specs = False
        if api_version_request.is_supported(
                req, flavors_view.FLAVOR_EXTRA_SPECS_MICROVERSION):
            include_extra_specs = context.can(
                fes_policies.POLICY_ROOT % 'index', fatal=False)
        include_description = api_version_request.is_supported(
            req, flavors_view.FLAVOR_DESCRIPTION_MICROVERSION)
        return self._view_builder.show(
            req, flavor, include_description=include_description,
            include_extra_specs=include_extra_specs)
Example #28
0
    def update(self, req, id, body):
        """Perform service update"""
        if api_version_request.is_supported(req, min_version="2.11"):
            actions = self.actions.copy()
            actions["force-down"] = self._forced_down
        else:
            actions = self.actions

        return self._perform_action(req, id, body, actions)
Example #29
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                # TODO(mriedem): Use a COUNT SQL query-based function instead
                # of InstanceList.get_uuids_by_host for performance.
                instance_uuids = objects.InstanceList.get_uuids_by_host(
                    context, service['host'])
                if instance_uuids:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                aggrs = self.aggregate_api.get_aggregates_by_host(context,
                                                                  service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(context,
                                                                  ag.id,
                                                                  service.host)
                # remove the corresponding resource provider record from
                # placement for this compute node
                self.placementclient.delete_resource_provider(
                    context, service.compute_node, cascade=True)
                # remove the host_mapping of this host.
                hm = objects.HostMapping.get_by_host(context, service.host)
                hm.destroy()
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
Example #30
0
File: common.py Project: mahak/nova
def supports_port_resource_request(req):
    """Check to see if the requested API version is high enough for resource
    request

    :param req: The incoming API request
    :returns: True if the requested API microversion is high enough for
        port resource request support, False otherwise.
    """
    return api_version_request.is_supported(req, '2.72')
Example #31
0
    def _get_hypervisors(self,
                         req,
                         detail=False,
                         limit=None,
                         marker=None,
                         links=False):
        """Get hypervisors for the given request.

        :param req: nova.api.openstack.wsgi.Request for the GET request
        :param detail: If True, return a detailed response.
        :param limit: An optional user-supplied page limit.
        :param marker: An optional user-supplied marker for paging.
        :param links: If True, return links in the response for paging.
        """
        context = req.environ['nova.context']
        context.can(hv_policies.BASE_POLICY_NAME)

        # The 2.53 microversion moves the search and servers routes into
        # GET /os-hypervisors and GET /os-hypervisors/detail with query
        # parameters.
        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            hypervisor_match = req.GET.get('hypervisor_hostname_pattern')
            with_servers = strutils.bool_from_string(req.GET.get(
                'with_servers', False),
                                                     strict=True)
        else:
            hypervisor_match = None
            with_servers = False

        if hypervisor_match is not None:
            # We have to check for 'limit' in the request itself because
            # the limit passed in is CONF.api.max_limit by default.
            if 'limit' in req.GET or marker:
                # Paging with hostname pattern isn't supported.
                raise webob.exc.HTTPBadRequest(
                    _('Paging over hypervisors with the '
                      'hypervisor_hostname_pattern query parameter is not '
                      'supported.'))

            # Explicitly do not try to generate links when querying with the
            # hostname pattern since the request in the link would fail the
            # check above.
            links = False

            # Get all compute nodes with a hypervisor_hostname that matches
            # the given pattern. If none are found then it's a 404 error.
            compute_nodes = self._get_compute_nodes_by_name_pattern(
                context, hypervisor_match)
        else:
            # Get all compute nodes.
            try:
                compute_nodes = self.host_api.compute_node_get_all(
                    context, limit=limit, marker=marker)
            except exception.MarkerNotFound:
                msg = _('marker [%s] not found') % marker
                raise webob.exc.HTTPBadRequest(explanation=msg)

        hypervisors_list = []
        for hyp in compute_nodes:
            try:
                instances = None
                if with_servers:
                    instances = self.host_api.instance_get_all_by_host(
                        context, hyp.host)
                service = self.host_api.service_get_by_compute_host(
                    context, hyp.host)
                hypervisors_list.append(
                    self._view_hypervisor(hyp,
                                          service,
                                          detail,
                                          req,
                                          servers=instances))
            except (exception.ComputeHostNotFound,
                    exception.HostMappingNotFound):
                # The compute service could be deleted which doesn't delete
                # the compute node record, that has to be manually removed
                # from the database so we just ignore it when listing nodes.
                LOG.debug(
                    'Unable to find service for compute node %s. The '
                    'service may be deleted and compute nodes need to '
                    'be manually cleaned up.', hyp.host)

        hypervisors_dict = dict(hypervisors=hypervisors_list)
        if links:
            hypervisors_links = self._view_builder.get_links(
                req, hypervisors_list, detail)
            if hypervisors_links:
                hypervisors_dict['hypervisors_links'] = hypervisors_links
        return hypervisors_dict
Example #32
0
    def show(self, request, instance, extend_address=True,
             show_extra_specs=None):
        """Detailed view of a single instance."""
        ip_v4 = instance.get('access_ip_v4')
        ip_v6 = instance.get('access_ip_v6')

        if show_extra_specs is None:
            # detail will pre-calculate this for us. If we're doing show,
            # then figure it out here.
            show_extra_specs = False
            if api_version_request.is_supported(request, min_version='2.47'):
                context = request.environ['nova.context']
                show_extra_specs = context.can(
                    fes_policies.POLICY_ROOT % 'index', fatal=False)

        server = {
            "server": {
                "id": instance["uuid"],
                "name": instance["display_name"],
                "status": self._get_vm_status(instance),
                "tenant_id": instance.get("project_id") or "",
                "user_id": instance.get("user_id") or "",
                "metadata": self._get_metadata(instance),
                "hostId": self._get_host_id(instance),
                "image": self._get_image(request, instance),
                "flavor": self._get_flavor(request, instance,
                                           show_extra_specs),
                "created": utils.isotime(instance["created_at"]),
                "updated": utils.isotime(instance["updated_at"]),
                "addresses": self._get_addresses(request, instance,
                                                 extend_address),
                "accessIPv4": str(ip_v4) if ip_v4 is not None else '',
                "accessIPv6": str(ip_v6) if ip_v6 is not None else '',
                "links": self._get_links(request,
                                         instance["uuid"],
                                         self._collection_name),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig": (
                    'AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True if instance["locked_by"]
                                          else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                                                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        return server
 def _get_on_shared_storage(self, req, evacuate_body):
     if api_version_request.is_supported(req, min_version='2.14'):
         return None
     else:
         return strutils.bool_from_string(evacuate_body["onSharedStorage"])
Example #34
0
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        context.can(ms_policies.POLICY_ROOT % 'migrate_live')

        host = body["os-migrateLive"]["host"]
        block_migration = body["os-migrateLive"]["block_migration"]
        force = None
        async_ = api_version_request.is_supported(req, min_version='2.34')
        if api_version_request.is_supported(req, min_version='2.30'):
            force = self._get_force_param_for_live_migration(body, host)
        if api_version_request.is_supported(req, min_version='2.25'):
            if block_migration == 'auto':
                block_migration = None
            else:
                block_migration = strutils.bool_from_string(block_migration,
                                                            strict=True)
            disk_over_commit = None
        else:
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]

            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)

        # NOTE(stephenfin): we need 'numa_topology' because of the
        # 'LiveMigrationTask._check_instance_has_no_numa' check in the
        # conductor
        instance = common.get_instance(self.compute_api, context, id,
                                       expected_attrs=['numa_topology'])
        try:
            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host, force,
                                          async_)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.NoValidHost,
                exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError) as ex:
            if async_:
                with excutils.save_and_reraise_exception():
                    LOG.error("Unexpected exception received from "
                              "conductor during pre-live-migration checks "
                              "'%(ex)s'", {'ex': ex})
            else:
                raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.InstanceIsLocked as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.ComputeHostNotFound as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'os-migrateLive', id)
Example #35
0
    def show(self, request, instance, extend_address=True):
        """Detailed view of a single instance."""
        ip_v4 = instance.get('access_ip_v4')
        ip_v6 = instance.get('access_ip_v6')
        #vm_hyper_type = instance.get('vm_hyper_type') or ""
        #import pdb
        #pdb.set_trace()
        server = {
            "server": {
                "id":
                instance["uuid"],
                "name":
                instance["display_name"],
                "status":
                self._get_vm_status(instance),
                "tenant_id":
                instance.get("project_id") or "",
                "user_id":
                instance.get("user_id") or "",
                "metadata":
                self._get_metadata(instance),
                "hostId":
                self._get_host_id(instance) or "",
                "image":
                self._get_image(request, instance),
                "flavor":
                self._get_flavor(request, instance),
                "created":
                utils.isotime(instance["created_at"]),
                "updated":
                utils.isotime(instance["updated_at"]),
                "vm_hypervisor_type":
                instance['vm_hyperv_type'],
                "addresses":
                self._get_addresses(request, instance, extend_address),
                "accessIPv4":
                str(ip_v4) if ip_v4 is not None else '',
                "accessIPv6":
                str(ip_v6) if ip_v6 is not None else '',
                "links":
                self._get_links(request, instance["uuid"],
                                self._collection_name),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig":
                ('AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True
                                          if instance["locked_by"] else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        return server
Example #36
0
    def _evacuate(self, req, id, body):
        """Permit admins to evacuate a server from a failed host
        to a new one.
        """
        context = req.environ["nova.context"]
        instance = common.get_instance(self.compute_api, context, id)
        context.can(evac_policies.BASE_POLICY_NAME,
                    target={
                        'user_id': instance.user_id,
                        'project_id': instance.project_id
                    })

        evacuate_body = body["evacuate"]
        host = evacuate_body.get("host")
        force = None

        on_shared_storage = self._get_on_shared_storage(req, evacuate_body)

        if api_version_request.is_supported(req, min_version='2.29'):
            force = body["evacuate"].get("force", False)
            force = strutils.bool_from_string(force, strict=True)
            if force is True and not host:
                message = _("Can't force to a non-provided destination")
                raise exc.HTTPBadRequest(explanation=message)
        if api_version_request.is_supported(req, min_version='2.14'):
            password = self._get_password_v214(req, evacuate_body)
        else:
            password = self._get_password(req, evacuate_body,
                                          on_shared_storage)

        if host is not None:
            try:
                self.host_api.service_get_by_compute_host(context, host)
            except (exception.ComputeHostNotFound,
                    exception.HostMappingNotFound):
                msg = _("Compute host %s not found.") % host
                raise exc.HTTPNotFound(explanation=msg)

        if instance.host == host:
            msg = _("The target host can't be the same one.")
            raise exc.HTTPBadRequest(explanation=msg)

        # We could potentially move this check to conductor and avoid the
        # extra API call to neutron when we support move operations with ports
        # having resource requests.
        if (common.instance_has_port_with_resource_request(
                context, instance.uuid, self.network_api) and
                not common.supports_port_resource_request_during_move(req)):
            msg = _("The evacuate action on a server with ports having "
                    "resource requests, like a port with a QoS minimum "
                    "bandwidth policy, is not supported with this "
                    "microversion")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            self.compute_api.evacuate(context, instance, host,
                                      on_shared_storage, password, force)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(
                state_error, 'evacuate', id)
        except exception.ComputeServiceInUse as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())

        if (not api_version_request.is_supported(req, min_version='2.14')
                and CONF.api.enable_instance_password):
            return {'adminPass': password}
        else:
            return None
Example #37
0
    def create(self, req, body):
        """Creates a new server group."""
        context = req.environ['nova.context']
        project_id = context.project_id
        context.can(sg_policies.POLICY_ROOT % 'create',
                    target={'project_id': project_id})
        try:
            objects.Quotas.check_deltas(context, {'server_groups': 1},
                                        project_id, context.user_id)
            local_limit.enforce_db_limit(context, local_limit.SERVER_GROUPS,
                                         entity_scope=project_id, delta=1)
        except nova.exception.ServerGroupLimitExceeded as e:
            raise exc.HTTPForbidden(explanation=str(e))
        except nova.exception.OverQuota:
            msg = _("Quota exceeded, too many server groups.")
            raise exc.HTTPForbidden(explanation=msg)

        vals = body['server_group']

        if api_version_request.is_supported(
                req, GROUP_POLICY_OBJ_MICROVERSION):
            policy = vals['policy']
            rules = vals.get('rules', {})
            if policy != 'anti-affinity' and rules:
                msg = _("Only anti-affinity policy supports rules.")
                raise exc.HTTPBadRequest(explanation=msg)
            # NOTE(yikun): This should be removed in Stein version.
            if not _should_enable_custom_max_server_rules(context, rules):
                msg = _("Creating an anti-affinity group with rule "
                        "max_server_per_host > 1 is not yet supported.")
                raise exc.HTTPConflict(explanation=msg)
            sg = objects.InstanceGroup(context, policy=policy,
                                       rules=rules)
        else:
            policies = vals.get('policies')
            sg = objects.InstanceGroup(context, policy=policies[0])
        try:
            sg.name = vals.get('name')
            sg.project_id = project_id
            sg.user_id = context.user_id
            sg.create()
        except ValueError as e:
            raise exc.HTTPBadRequest(explanation=e)

        # NOTE(melwitt): We recheck the quota after creating the object to
        # prevent users from allocating more resources than their allowed quota
        # in the event of a race. This is configurable because it can be
        # expensive if strict quota limits are not required in a deployment.
        if CONF.quota.recheck_quota:
            try:
                objects.Quotas.check_deltas(context, {'server_groups': 0},
                                            project_id,
                                            context.user_id)
                # TODO(johngarbutt): decide if we need this recheck
                # The quota rechecking of limits is really just to protect
                # against denial of service attacks that aim to fill up the
                # database. Its usefulness could be debated.
                local_limit.enforce_db_limit(context,
                                             local_limit.SERVER_GROUPS,
                                             project_id, delta=0)
            except nova.exception.ServerGroupLimitExceeded as e:
                sg.destroy()
                raise exc.HTTPForbidden(explanation=str(e))
            except nova.exception.OverQuota:
                sg.destroy()
                msg = _("Quota exceeded, too many server groups.")
                raise exc.HTTPForbidden(explanation=msg)

        return {'server_group': self._format_server_group(context, sg, req)}
Example #38
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                num_instances = objects.InstanceList.get_count_by_hosts(
                    context, [service['host']])
                if num_instances:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                # Similarly, check to see if the are any in-progress migrations
                # involving this host because if there are we need to block the
                # service delete since we could orphan resource providers and
                # break the ability to do things like confirm/revert instances
                # in VERIFY_RESIZE status.
                compute_nodes = objects.ComputeNodeList.get_all_by_host(
                    context, service.host)
                self._assert_no_in_progress_migrations(
                    context, id, compute_nodes)

                aggrs = self.aggregate_api.get_aggregates_by_host(context,
                                                                  service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(context,
                                                                  ag.id,
                                                                  service.host)
                # remove the corresponding resource provider record from
                # placement for the compute nodes managed by this service;
                # remember that an ironic compute service can manage multiple
                # nodes
                for compute_node in compute_nodes:
                    try:
                        self.placementclient.delete_resource_provider(
                            context, compute_node, cascade=True)
                    except ks_exc.ClientException as e:
                        LOG.error(
                            "Failed to delete compute node resource provider "
                            "for compute node %s: %s",
                            compute_node.uuid, six.text_type(e))
                # remove the host_mapping of this host.
                try:
                    hm = objects.HostMapping.get_by_host(context, service.host)
                    hm.destroy()
                except exception.HostMappingNotFound:
                    # It's possible to startup a nova-compute service and then
                    # delete it (maybe it was accidental?) before mapping it to
                    # a cell using discover_hosts, so we just ignore this.
                    pass
            service.destroy()

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
Example #39
0
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        context.can(ms_policies.POLICY_ROOT % 'migrate_live')

        host = body["os-migrateLive"]["host"]
        block_migration = body["os-migrateLive"]["block_migration"]
        force = None
        async = api_version_request.is_supported(req, min_version='2.34')
        if api_version_request.is_supported(req, min_version='2.30'):
            force = self._get_force_param_for_live_migration(body, host)
        if api_version_request.is_supported(req, min_version='2.25'):
            if block_migration == 'auto':
                block_migration = None
            else:
                block_migration = strutils.bool_from_string(block_migration,
                                                            strict=True)
            disk_over_commit = None
        else:
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]

            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)

        instance = common.get_instance(self.compute_api, context, id)
        try:
            # WRS: Live migration with pci devices is only supported with
            # macvtap (which is currently not supported in Titanium Cloud).
            if instance.pci_devices:
                msg = _("Live migration of instance %s is not supported "
                        "with PCI devices attached.") % id
                raise exc.HTTPBadRequest(explanation=msg)

            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host, force, async)
        except exception.InstanceUnknownCell as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.NoValidHost,
                exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError,
                exception.MigrationPreCheckErrorNoRetry,
                exception.LiveMigrationWithOldNovaNotSupported) as ex:
            if async:
                with excutils.save_and_reraise_exception():
                    LOG.error("Unexpected exception received from "
                              "conductor during pre-live-migration checks "
                              "'%(ex)s'", {'ex': ex})
            else:
                raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.InstanceIsLocked as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.ComputeHostNotFound as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'os-migrateLive', id)
Example #40
0
    def delete(self, req, id):
        """Deletes the specified service."""
        context = req.environ['nova.context']
        context.can(services_policies.BASE_POLICY_NAME)

        if api_version_request.is_supported(
                req, min_version=UUID_FOR_ID_MIN_VERSION):
            if not uuidutils.is_uuid_like(id):
                msg = _('Invalid uuid %s') % id
                raise webob.exc.HTTPBadRequest(explanation=msg)
        else:
            try:
                utils.validate_integer(id, 'id')
            except exception.InvalidInput as exc:
                raise webob.exc.HTTPBadRequest(
                    explanation=exc.format_message())

        try:
            service = self.host_api.service_get_by_id(context, id)
            # remove the service from all the aggregates in which it's included
            if service.binary == 'nova-compute':
                # Check to see if there are any instances on this compute host
                # because if there are, we need to block the service (and
                # related compute_nodes record) delete since it will impact
                # resource accounting in Placement and orphan the compute node
                # resource provider.
                # TODO(mriedem): Use a COUNT SQL query-based function instead
                # of InstanceList.get_uuids_by_host for performance.
                instance_uuids = objects.InstanceList.get_uuids_by_host(
                    context, service['host'])
                if instance_uuids:
                    raise webob.exc.HTTPConflict(
                        explanation=_('Unable to delete compute service that '
                                      'is hosting instances. Migrate or '
                                      'delete the instances first.'))

                aggrs = self.aggregate_api.get_aggregates_by_host(
                    context, service.host)
                for ag in aggrs:
                    self.aggregate_api.remove_host_from_aggregate(
                        context, ag.id, service.host)
                # remove the corresponding resource provider record from
                # placement for this compute node
                self.placementclient.delete_resource_provider(
                    context, service.compute_node, cascade=True)
                # remove the host_mapping of this host.
                try:
                    hm = objects.HostMapping.get_by_host(context, service.host)
                    hm.destroy()
                except exception.HostMappingNotFound:
                    # It's possible to startup a nova-compute service and then
                    # delete it (maybe it was accidental?) before mapping it to
                    # a cell using discover_hosts, so we just ignore this.
                    pass
            self.host_api.service_delete(context, id)

        except exception.ServiceNotFound:
            explanation = _("Service %s not found.") % id
            raise webob.exc.HTTPNotFound(explanation=explanation)
        except exception.ServiceNotUnique:
            explanation = _("Service id %s refers to multiple services.") % id
            raise webob.exc.HTTPBadRequest(explanation=explanation)
    def show(self, req, server_id, id):
        """Return data about the given instance action."""
        context = req.environ['nova.context']
        instance = self._get_instance(req, context, server_id)
        context.can(ia_policies.BASE_POLICY_NAME % 'show',
                    target={'project_id': instance.project_id})
        action = self.action_api.action_get_by_request_id(
            context, instance, id)
        if action is None:
            msg = _("Action %s not found") % id
            raise exc.HTTPNotFound(explanation=msg)

        action_id = action['id']
        if api_version_request.is_supported(req, min_version="2.58"):
            action = self._format_action(action, ACTION_KEYS_V258)
        else:
            action = self._format_action(action, ACTION_KEYS)
        # Prior to microversion 2.51, events would only be returned in the
        # response for admins by default policy rules. Starting in
        # microversion 2.51, events are returned for admin_or_owner (of the
        # instance) but the "traceback" field is only shown for admin users
        # by default.
        show_events = False
        show_traceback = False
        show_host = False
        if context.can(ia_policies.BASE_POLICY_NAME % 'events',
                       target={'project_id': instance.project_id},
                       fatal=False):
            # For all microversions, the user can see all event details
            # including the traceback.
            show_events = show_traceback = True
            show_host = api_version_request.is_supported(req, '2.62')
        elif api_version_request.is_supported(req, '2.51'):
            # The user is not able to see all event details, but they can at
            # least see the non-traceback event details.
            show_events = True

        # An obfuscated hashed host id is returned since microversion 2.62
        # for all users.
        show_hostid = api_version_request.is_supported(req, '2.62')

        if show_events:
            # NOTE(brinzhang): Event details are shown since microversion
            # 2.84.
            show_details = False
            support_v284 = api_version_request.is_supported(req, '2.84')
            if support_v284:
                show_details = context.can(
                    ia_policies.BASE_POLICY_NAME % 'events:details',
                    target={'project_id': instance.project_id},
                    fatal=False)

            events_raw = self.action_api.action_events_get(
                context, instance, action_id)
            # NOTE(takashin): The project IDs of instance action events
            # become null (None) when instance action events are created
            # by periodic tasks. If the project ID is null (None),
            # it causes an error when 'hostId' is generated.
            # If the project ID is null (None), pass the project ID of
            # the server instead of that of instance action events.
            action['events'] = [
                self._format_event(evt,
                                   action['project_id'] or instance.project_id,
                                   show_traceback=show_traceback,
                                   show_host=show_host,
                                   show_hostid=show_hostid,
                                   show_details=show_details)
                for evt in events_raw
            ]
        return {'instanceAction': action}
Example #42
0
    def _index(self,
               req,
               add_link=False,
               next_link=False,
               add_uuid=False,
               sort_dirs=None,
               sort_keys=None,
               limit=None,
               marker=None,
               allow_changes_since=False,
               allow_changes_before=False):
        context = req.environ['nova.context']
        context.can(migrations_policies.POLICY_ROOT % 'index', target={})
        search_opts = {}
        search_opts.update(req.GET)
        if 'changes-since' in search_opts:
            if allow_changes_since:
                search_opts['changes-since'] = timeutils.parse_isotime(
                    search_opts['changes-since'])
            else:
                # Before microversion 2.59, the changes-since filter was not
                # supported in the DB API. However, the schema allowed
                # additionalProperties=True, so a user could pass it before
                # 2.59 and filter by the updated_at field if we don't remove
                # it from search_opts.
                del search_opts['changes-since']

        if 'changes-before' in search_opts:
            if allow_changes_before:
                search_opts['changes-before'] = timeutils.parse_isotime(
                    search_opts['changes-before'])
                changes_since = search_opts.get('changes-since')
                if (changes_since and search_opts['changes-before'] <
                        search_opts['changes-since']):
                    msg = _('The value of changes-since must be less than '
                            'or equal to changes-before.')
                    raise exc.HTTPBadRequest(explanation=msg)
            else:
                # Before microversion 2.59 the schema allowed
                # additionalProperties=True, so a user could pass
                # changes-before before 2.59 and filter by the updated_at
                # field if we don't remove it from search_opts.
                del search_opts['changes-before']

        if sort_keys:
            try:
                migrations = self.compute_api.get_migrations_sorted(
                    context,
                    search_opts,
                    sort_dirs=sort_dirs,
                    sort_keys=sort_keys,
                    limit=limit,
                    marker=marker)
            except exception.MarkerNotFound as e:
                raise exc.HTTPBadRequest(explanation=e.format_message())
        else:
            migrations = self.compute_api.get_migrations(context, search_opts)

        add_user_project = api_version_request.is_supported(req, '2.80')
        migrations = self._output(req, migrations, add_link, add_uuid,
                                  add_user_project)
        migrations_dict = {'migrations': migrations}

        if next_link:
            migrations_links = self._view_builder.get_links(req, migrations)
            if migrations_links:
                migrations_dict['migrations_links'] = migrations_links
        return migrations_dict
Example #43
0
 def _allow_bfv_rescue(self):
     return api_version_request.is_supported(self.fake_req, '2.87')
Example #44
0
    def _get_servers(self, req, is_detail):
        """Returns a list of servers, based on any search options specified."""

        search_opts = {}
        search_opts.update(req.GET)

        context = req.environ['nova.context']
        remove_invalid_options(context, search_opts,
                self._get_server_search_options(req))

        # Verify search by 'status' contains a valid status.
        # Convert it to filter by vm_state or task_state for compute_api.
        search_opts.pop('status', None)
        if 'status' in req.GET.keys():
            statuses = req.GET.getall('status')
            states = common.task_and_vm_state_from_status(statuses)
            vm_state, task_state = states
            if not vm_state and not task_state:
                return {'servers': []}
            search_opts['vm_state'] = vm_state
            # When we search by vm state, task state will return 'default'.
            # So we don't need task_state search_opt.
            if 'default' not in task_state:
                search_opts['task_state'] = task_state

        if 'changes-since' in search_opts:
            try:
                parsed = timeutils.parse_isotime(search_opts['changes-since'])
            except ValueError:
                msg = _('Invalid changes-since value')
                raise exc.HTTPBadRequest(explanation=msg)
            search_opts['changes-since'] = parsed

        # By default, compute's get_all() will return deleted instances.
        # If an admin hasn't specified a 'deleted' search option, we need
        # to filter out deleted instances by setting the filter ourselves.
        # ... Unless 'changes-since' is specified, because 'changes-since'
        # should return recently deleted instances according to the API spec.

        if 'deleted' not in search_opts:
            if 'changes-since' not in search_opts:
                # No 'changes-since', so we only want non-deleted servers
                search_opts['deleted'] = False
        else:
            # Convert deleted filter value to a valid boolean.
            # Return non-deleted servers if an invalid value
            # is passed with deleted filter.
            search_opts['deleted'] = strutils.bool_from_string(
                search_opts['deleted'], default=False)

        if search_opts.get("vm_state") == ['deleted']:
            if context.is_admin:
                search_opts['deleted'] = True
            else:
                msg = _("Only administrators may list deleted instances")
                raise exc.HTTPForbidden(explanation=msg)

        if api_version_request.is_supported(req, min_version='2.26'):
            for tag_filter in TAG_SEARCH_FILTERS:
                if tag_filter in search_opts:
                    search_opts[tag_filter] = search_opts[
                        tag_filter].split(',')

        # If tenant_id is passed as a search parameter this should
        # imply that all_tenants is also enabled unless explicitly
        # disabled. Note that the tenant_id parameter is filtered out
        # by remove_invalid_options above unless the requestor is an
        # admin.

        # TODO(gmann): 'all_tenants' flag should not be required while
        # searching with 'tenant_id'. Ref bug# 1185290
        # +microversions to achieve above mentioned behavior by
        # uncommenting below code.

        # if 'tenant_id' in search_opts and 'all_tenants' not in search_opts:
            # We do not need to add the all_tenants flag if the tenant
            # id associated with the token is the tenant id
            # specified. This is done so a request that does not need
            # the all_tenants flag does not fail because of lack of
            # policy permission for compute:get_all_tenants when it
            # doesn't actually need it.
            # if context.project_id != search_opts.get('tenant_id'):
            #    search_opts['all_tenants'] = 1

        all_tenants = common.is_all_tenants(search_opts)
        # use the boolean from here on out so remove the entry from search_opts
        # if it's present
        search_opts.pop('all_tenants', None)

        elevated = None
        if all_tenants:
            if is_detail:
                context.can(server_policies.SERVERS % 'detail:get_all_tenants')
            else:
                context.can(server_policies.SERVERS % 'index:get_all_tenants')
            elevated = context.elevated()
        else:
            if context.project_id:
                search_opts['project_id'] = context.project_id
            else:
                search_opts['user_id'] = context.user_id

        limit, marker = common.get_limit_and_marker(req)
        sort_keys, sort_dirs = common.get_sort_params(req.params)

        expected_attrs = ['pci_devices']
        if is_detail:
            if api_version_request.is_supported(req, '2.26'):
                expected_attrs.append("tags")

            # merge our expected attrs with what the view builder needs for
            # showing details
            expected_attrs = self._view_builder.get_show_expected_attrs(
                                                                expected_attrs)

        try:
            instance_list = self.compute_api.get_all(elevated or context,
                    search_opts=search_opts, limit=limit, marker=marker,
                    expected_attrs=expected_attrs,
                    sort_keys=sort_keys, sort_dirs=sort_dirs)
        except exception.MarkerNotFound:
            msg = _('marker [%s] not found') % marker
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.FlavorNotFound:
            LOG.debug("Flavor '%s' could not be found ",
                      search_opts['flavor'])
            instance_list = objects.InstanceList()

        if is_detail:
            instance_list._context = context
            instance_list.fill_faults()
            response = self._view_builder.detail(req, instance_list)
        else:
            response = self._view_builder.index(req, instance_list)
        req.cache_db_instances(instance_list)
        return response
Example #45
0
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        context.can(ms_policies.POLICY_ROOT % 'migrate_live')

        host = body["os-migrateLive"]["host"]
        block_migration = body["os-migrateLive"]["block_migration"]
        force = None
        async_ = api_version_request.is_supported(req, min_version='2.34')
        if api_version_request.is_supported(req, min_version='2.30'):
            force = self._get_force_param_for_live_migration(body, host)
        if api_version_request.is_supported(req, min_version='2.25'):
            if block_migration == 'auto':
                block_migration = None
            else:
                block_migration = strutils.bool_from_string(block_migration,
                                                            strict=True)
            disk_over_commit = None
        else:
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]

            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)

        # NOTE(stephenfin): we need 'numa_topology' because of the
        # 'LiveMigrationTask._check_instance_has_no_numa' check in the
        # conductor
        instance = common.get_instance(self.compute_api,
                                       context,
                                       id,
                                       expected_attrs=['numa_topology'])

        # We could potentially move this check to conductor and avoid the
        # extra API call to neutron when we support move operations with ports
        # having resource requests.
        if (common.instance_has_port_with_resource_request(
                instance.uuid, self.network_api) and
                not common.supports_port_resource_request_during_move(req)):
            msg = _("The os-migrateLive action on a server with ports having "
                    "resource requests, like a port with a QoS minimum "
                    "bandwidth policy, is not supported with this "
                    "microversion")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host, force,
                                          async_)
        except (exception.NoValidHost, exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType, exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage, exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError) as ex:
            if async_:
                with excutils.save_and_reraise_exception():
                    LOG.error(
                        "Unexpected exception received from "
                        "conductor during pre-live-migration checks "
                        "'%(ex)s'", {'ex': ex})
            else:
                raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.OperationNotSupportedForSEV as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.InstanceIsLocked as e:
            raise exc.HTTPConflict(explanation=e.format_message())
        except exception.ComputeHostNotFound as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(
                state_error, 'os-migrateLive', id)
Example #46
0
 def _get_services_list(self, req, additional_fields=()):
     _services = self._get_services(req)
     cell_down_support = api_version_request.is_supported(req,
         min_version=PARTIAL_CONSTRUCT_FOR_CELL_DOWN_MIN_VERSION)
     return [self._get_service_detail(svc, additional_fields, req,
             cell_down_support=cell_down_support) for svc in _services]
Example #47
0
    def show(self,
             request,
             instance,
             extend_address=True,
             show_extra_specs=None,
             show_AZ=True,
             show_config_drive=True,
             show_extended_attr=None,
             show_host_status=None,
             show_keypair=True,
             show_srv_usg=True,
             show_sec_grp=True,
             show_extended_status=True,
             show_extended_volumes=True,
             bdms=None,
             cell_down_support=False,
             show_server_groups=False,
             show_user_data=True):
        """Detailed view of a single instance."""
        if show_extra_specs is None:
            # detail will pre-calculate this for us. If we're doing show,
            # then figure it out here.
            show_extra_specs = False
            if api_version_request.is_supported(request, min_version='2.47'):
                context = request.environ['nova.context']
                show_extra_specs = context.can(fes_policies.POLICY_ROOT %
                                               'index',
                                               fatal=False)

        if cell_down_support and 'display_name' not in instance:
            # NOTE(tssurya): If the microversion is >= 2.69, this boolean will
            # be true in which case we check if there are instances from down
            # cells (by checking if their objects have missing keys like
            # `display_name`) and return partial constructs based on the
            # information available from the nova_api database.
            return self._show_from_down_cell(request, instance,
                                             show_extra_specs,
                                             show_server_groups)
        ip_v4 = instance.get('access_ip_v4')
        ip_v6 = instance.get('access_ip_v6')

        server = {
            "server": {
                "id":
                instance["uuid"],
                "name":
                instance["display_name"],
                "status":
                self._get_vm_status(instance),
                "tenant_id":
                instance.get("project_id") or "",
                "user_id":
                instance.get("user_id") or "",
                "metadata":
                self._get_metadata(instance),
                "hostId":
                self._get_host_id(instance),
                "image":
                self._get_image(request, instance),
                "flavor":
                self._get_flavor(request, instance, show_extra_specs),
                "created":
                utils.isotime(instance["created_at"]),
                "updated":
                utils.isotime(instance["updated_at"]),
                "addresses":
                self._get_addresses(request, instance, extend_address),
                "accessIPv4":
                str(ip_v4) if ip_v4 is not None else '',
                "accessIPv6":
                str(ip_v6) if ip_v6 is not None else '',
                "links":
                self._get_links(request, instance["uuid"],
                                self._collection_name),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig":
                ('AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        context = request.environ['nova.context']
        if show_AZ:
            az = avail_zone.get_instance_availability_zone(context, instance)
            # NOTE(mriedem): The OS-EXT-AZ prefix should not be used for new
            # attributes after v2.1. They are only in v2.1 for backward compat
            # with v2.0.
            server["server"]["OS-EXT-AZ:availability_zone"] = az or ''

        if show_config_drive:
            server["server"]["config_drive"] = instance["config_drive"]

        if show_keypair:
            server["server"]["key_name"] = instance["key_name"]

        if show_srv_usg:
            for k in ['launched_at', 'terminated_at']:
                key = "OS-SRV-USG:" + k
                # NOTE(danms): Historically, this timestamp has been generated
                # merely by grabbing str(datetime) of a TZ-naive object. The
                # only way we can keep that with instance objects is to strip
                # the tzinfo from the stamp and str() it.
                server["server"][key] = (instance[k].replace(
                    tzinfo=None) if instance[k] else None)
        if show_sec_grp:
            self._add_security_grps(request, [server["server"]], [instance])

        if show_extended_attr is None:
            show_extended_attr = context.can(
                esa_policies.BASE_POLICY_NAME,
                fatal=False,
                target={'project_id': instance.project_id})

        if show_extended_attr:
            properties = ['host', 'name', 'node']
            if api_version_request.is_supported(request, min_version='2.3'):
                # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix
                # below and that's OK for microversion 2.3 which is being
                # compatible with v2.0 for the ec2 API split out from Nova.
                # After this, however, new microversions should not be using
                # the OS-EXT-SRV-ATTR prefix.
                properties += [
                    'reservation_id', 'launch_index', 'hostname', 'kernel_id',
                    'ramdisk_id', 'root_device_name'
                ]
                # NOTE(gmann): Since microversion 2.75, PUT and Rebuild
                # response include all the server attributes including these
                # extended attributes also. But microversion 2.57 already
                # adding the 'user_data' in Rebuild response in API method.
                # so we will skip adding the user data attribute for rebuild
                # case. 'show_user_data' is false only in case of rebuild.
                if show_user_data:
                    properties += ['user_data']
            for attr in properties:
                if attr == 'name':
                    key = "OS-EXT-SRV-ATTR:instance_%s" % attr
                elif attr == 'node':
                    key = "OS-EXT-SRV-ATTR:hypervisor_hostname"
                else:
                    # NOTE(mriedem): Nothing after microversion 2.3 should use
                    # the OS-EXT-SRV-ATTR prefix for the attribute key name.
                    key = "OS-EXT-SRV-ATTR:%s" % attr
                server["server"][key] = getattr(instance, attr)

        if show_extended_status:
            # NOTE(gmann): Removed 'locked_by' from extended status
            # to make it same as V2. If needed it can be added with
            # microversion.
            for state in ['task_state', 'vm_state', 'power_state']:
                # NOTE(mriedem): The OS-EXT-STS prefix should not be used for
                # new attributes after v2.1. They are only in v2.1 for backward
                # compat with v2.0.
                key = "%s:%s" % ('OS-EXT-STS', state)
                server["server"][key] = instance[state]

        if show_extended_volumes:
            # NOTE(mriedem): The os-extended-volumes prefix should not be used
            # for new attributes after v2.1. They are only in v2.1 for backward
            # compat with v2.0.
            add_delete_on_termination = api_version_request.is_supported(
                request, min_version='2.3')
            if bdms is None:
                bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
                    context, [instance["uuid"]])
            self._add_volumes_attachments(server["server"], bdms,
                                          add_delete_on_termination)

        if api_version_request.is_supported(request, min_version='2.16'):
            if show_host_status is None:
                unknown_only = self._get_host_status_unknown_only(
                    context, instance)
                # If we're not allowed by policy to show host status at all,
                # don't bother requesting instance host status from the compute
                # API.
                if unknown_only is not None:
                    host_status = self.compute_api.get_instance_host_status(
                        instance)
                    # If we are allowed to show host status of some kind, set
                    # the host status field only if:
                    #   * unknown_only = False, meaning we can show any status
                    # OR
                    #   * if unknown_only = True and host_status == UNKNOWN
                    if (not unknown_only
                            or host_status == fields.HostStatus.UNKNOWN):
                        server["server"]['host_status'] = host_status

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True
                                          if instance["locked_by"] else False)

        if api_version_request.is_supported(request, min_version="2.73"):
            server["server"]["locked_reason"] = (
                instance.system_metadata.get("locked_reason"))

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        if api_version_request.is_supported(request, min_version="2.63"):
            trusted_certs = None
            if instance.trusted_certs:
                trusted_certs = instance.trusted_certs.ids
            server["server"]["trusted_image_certificates"] = trusted_certs

        # TODO(stephenfin): Remove this check once we remove the
        # OS-EXT-SRV-ATTR:hostname policy checks from the policy is Y or later
        if api_version_request.is_supported(request, min_version='2.90'):
            # API 2.90 made this field visible to non-admins, but we only show
            # it if it's not already added
            if not show_extended_attr:
                server["server"]["OS-EXT-SRV-ATTR:hostname"] = \
                    instance.hostname

        if show_server_groups:
            server['server']['server_groups'] = self._get_server_groups(
                context, instance)
        return server
Example #48
0
    def show(self, request, instance, extend_address=True,
             show_extra_specs=None, show_AZ=True, show_config_drive=True,
             show_extended_attr=None, show_host_status=None,
             show_keypair=True, show_srv_usg=True, show_sec_grp=True,
             show_extended_status=True, show_extended_volumes=True,
             bdms=None):
        """Detailed view of a single instance."""
        ip_v4 = instance.get('access_ip_v4')
        ip_v6 = instance.get('access_ip_v6')

        if show_extra_specs is None:
            # detail will pre-calculate this for us. If we're doing show,
            # then figure it out here.
            show_extra_specs = False
            if api_version_request.is_supported(request, min_version='2.47'):
                context = request.environ['nova.context']
                show_extra_specs = context.can(
                    fes_policies.POLICY_ROOT % 'index', fatal=False)

        server = {
            "server": {
                "id": instance["uuid"],
                "name": instance["display_name"],
                "status": self._get_vm_status(instance),
                "tenant_id": instance.get("project_id") or "",
                "user_id": instance.get("user_id") or "",
                "metadata": self._get_metadata(instance),
                "hostId": self._get_host_id(instance),
                "image": self._get_image(request, instance),
                "flavor": self._get_flavor(request, instance,
                                           show_extra_specs),
                "created": utils.isotime(instance["created_at"]),
                "updated": utils.isotime(instance["updated_at"]),
                "addresses": self._get_addresses(request, instance,
                                                 extend_address),
                "accessIPv4": str(ip_v4) if ip_v4 is not None else '',
                "accessIPv6": str(ip_v6) if ip_v6 is not None else '',
                "links": self._get_links(request,
                                         instance["uuid"],
                                         self._collection_name),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig": (
                    'AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        context = request.environ['nova.context']
        if show_AZ:
            az = avail_zone.get_instance_availability_zone(context, instance)
            # NOTE(mriedem): The OS-EXT-AZ prefix should not be used for new
            # attributes after v2.1. They are only in v2.1 for backward compat
            # with v2.0.
            server["server"]["OS-EXT-AZ:availability_zone"] = az or ''

        if show_config_drive:
            server["server"]["config_drive"] = instance["config_drive"]

        if show_keypair:
            server["server"]["key_name"] = instance["key_name"]

        if show_srv_usg:
            for k in ['launched_at', 'terminated_at']:
                key = "OS-SRV-USG:" + k
                # NOTE(danms): Historically, this timestamp has been generated
                # merely by grabbing str(datetime) of a TZ-naive object. The
                # only way we can keep that with instance objects is to strip
                # the tzinfo from the stamp and str() it.
                server["server"][key] = (instance[k].replace(tzinfo=None)
                                         if instance[k] else None)
        if show_sec_grp:
            self._add_security_grps(request, [server["server"]], [instance])

        if show_extended_attr is None:
            show_extended_attr = context.can(
                esa_policies.BASE_POLICY_NAME, fatal=False)
        if show_extended_attr:
            server["server"][
                "OS-EXT-SRV-ATTR:hypervisor_hostname"] = instance.node

            properties = ['host', 'name']
            if api_version_request.is_supported(request, min_version='2.3'):
                # NOTE(mriedem): These will use the OS-EXT-SRV-ATTR prefix
                # below and that's OK for microversion 2.3 which is being
                # compatible with v2.0 for the ec2 API split out from Nova.
                # After this, however, new microversions should not be using
                # the OS-EXT-SRV-ATTR prefix.
                properties += ['reservation_id', 'launch_index',
                               'hostname', 'kernel_id', 'ramdisk_id',
                               'root_device_name', 'user_data']
            for attr in properties:
                if attr == 'name':
                    key = "OS-EXT-SRV-ATTR:instance_%s" % attr
                else:
                    # NOTE(mriedem): Nothing after microversion 2.3 should use
                    # the OS-EXT-SRV-ATTR prefix for the attribute key name.
                    key = "OS-EXT-SRV-ATTR:%s" % attr
                server["server"][key] = instance[attr]
        if show_extended_status:
            # NOTE(gmann): Removed 'locked_by' from extended status
            # to make it same as V2. If needed it can be added with
            # microversion.
            for state in ['task_state', 'vm_state', 'power_state']:
                # NOTE(mriedem): The OS-EXT-STS prefix should not be used for
                # new attributes after v2.1. They are only in v2.1 for backward
                # compat with v2.0.
                key = "%s:%s" % ('OS-EXT-STS', state)
                server["server"][key] = instance[state]
        if show_extended_volumes:
            # NOTE(mriedem): The os-extended-volumes prefix should not be used
            # for new attributes after v2.1. They are only in v2.1 for backward
            # compat with v2.0.
            add_delete_on_termination = api_version_request.is_supported(
                request, min_version='2.3')
            if bdms is None:
                bdms = objects.BlockDeviceMappingList.bdms_by_instance_uuid(
                    context, [instance["uuid"]])
            self._add_volumes_attachments(server["server"],
                                          bdms,
                                          add_delete_on_termination)
        if (api_version_request.is_supported(request, min_version='2.16')):
            if show_host_status is None:
                show_host_status = context.can(
                    servers_policies.SERVERS % 'show:host_status', fatal=False)
            if show_host_status:
                host_status = self.compute_api.get_instance_host_status(
                                  instance)
                server["server"]['host_status'] = host_status

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True if instance["locked_by"]
                                          else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                                                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        if api_version_request.is_supported(request, min_version="2.63"):
            trusted_certs = None
            if instance.trusted_certs:
                trusted_certs = instance.trusted_certs.ids
            server["server"]["trusted_image_certificates"] = trusted_certs

        return server
Example #49
0
    def show(self, request, instance, extend_address=True):
        """Detailed view of a single instance."""
        server = {
            "server": {
                "id":
                instance["uuid"],
                "name":
                instance["display_name"],
                "root_device_name":
                instance["root_device_name"],
                "root_gb":
                instance["root_gb"],
                "status":
                self._get_vm_status(instance),
                "tenant_id":
                instance.get("project_id") or "",
                "user_id":
                instance.get("user_id") or "",
                "metadata":
                self._get_metadata(instance),
                "hostId":
                self._get_host_id(instance) or "",
                # TODO(alex_xu): '_get_image' return {} when there image_ref
                # isn't existed in V3 API, we revert it back to return "" in
                # V2.1.
                "image":
                self._get_image(request, instance),
                "flavor":
                self._get_flavor(request, instance),
                "created":
                utils.isotime(instance["created_at"]),
                "updated":
                utils.isotime(instance["updated_at"]),
                "addresses":
                self._get_addresses(request, instance, extend_address),
                #begin:<ligh4>:<bug 75710(refer to Bugzilla)>:<action(a)>:<date(2016-11-15)>
                "host-compute-service-status":
                instance["host-compute-service-status"],
                #end:<ligh4>:<bug 75710(refer to Bugzilla)>:<action(a)>:<date(2016-11-15)
                "links":
                self._get_links(request, instance["uuid"],
                                self._collection_name),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True
                                          if instance["locked_by"] else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                "display_description")

        return server
Example #50
0
    def show(self, request, instance, extend_address=True):
        """Detailed view of a single instance."""
        server = {
            "server": {
                "id":
                instance["uuid"],
                "name":
                instance["display_name"],
                "status":
                self._get_vm_status(instance),
                "tenant_id":
                instance.get("project_id") or "",
                "user_id":
                instance.get("user_id") or "",
                "metadata":
                self._get_metadata(instance),
                "hostId":
                self._get_host_id(instance) or "",
                # TODO(alex_xu): '_get_image' return {} when there image_ref
                # isn't existed in V3 API, we revert it back to return "" in
                # V2.1.
                "image":
                self._get_image(request, instance),
                "flavor":
                self._get_flavor(request, instance),
                # NOTE(sdague): historically this was the
                # os-disk-config extension, but now that extensions
                # are gone, we merge these attributes here.
                "OS-DCF:diskConfig":
                ('AUTO' if instance.get('auto_disk_config') else 'MANUAL'),
                "created":
                utils.isotime(instance["created_at"]),
                "updated":
                utils.isotime(instance["updated_at"]),
                "addresses":
                self._get_addresses(request, instance, extend_address),
                "links":
                self._get_links(request, instance["uuid"],
                                self._collection_name),
            },
        }
        if server["server"]["status"] in self._fault_statuses:
            _inst_fault = self._get_fault(request, instance)
            if _inst_fault:
                server['server']['fault'] = _inst_fault

        if server["server"]["status"] in self._progress_statuses:
            server["server"]["progress"] = instance.get("progress", 0)

        if api_version_request.is_supported(request, min_version="2.9"):
            server["server"]["locked"] = (True
                                          if instance["locked_by"] else False)

        if api_version_request.is_supported(request, min_version="2.19"):
            server["server"]["description"] = instance.get(
                "display_description")

        if api_version_request.is_supported(request, min_version="2.26"):
            server["server"]["tags"] = [t.tag for t in instance.tags]

        return server
Example #51
0
    def create(self, req, body):
        """Creates a new server for a given user."""

        context = req.environ['nova.context']
        server_dict = body['server']
        password = self._get_server_admin_password(server_dict)
        name = common.normalize_name(server_dict['name'])

        if api_version_request.is_supported(req, min_version='2.19'):
            if 'description' in server_dict:
                # This is allowed to be None
                description = server_dict['description']
            else:
                # No default description
                description = None
        else:
            description = name

        # Arguments to be passed to instance create function
        create_kwargs = {}

        # Query extensions which want to manipulate the keyword
        # arguments.
        # NOTE(cyeoh): This is the hook that extensions use
        # to replace the extension specific code below.
        # When the extensions are ported this will also result
        # in some convenience function from this class being
        # moved to the extension
        if list(self.create_extension_manager):
            self.create_extension_manager.map(self._create_extension_point,
                                              server_dict, create_kwargs, body)

        availability_zone = create_kwargs.pop("availability_zone", None)

        helpers.translate_attributes(helpers.CREATE,
                                     server_dict, create_kwargs)

        target = {
            'project_id': context.project_id,
            'user_id': context.user_id,
            'availability_zone': availability_zone}
        context.can(server_policies.SERVERS % 'create', target)

        # TODO(Shao He, Feng) move this policy check to os-availability-zone
        # extension after refactor it.
        parse_az = self.compute_api.parse_availability_zone
        try:
            availability_zone, host, node = parse_az(context,
                                                     availability_zone)
        except exception.InvalidInput as err:
            raise exc.HTTPBadRequest(explanation=six.text_type(err))
        if host or node:
            context.can(server_policies.SERVERS % 'create:forced_host', {})

        block_device_mapping = create_kwargs.get("block_device_mapping")
        # TODO(Shao He, Feng) move this policy check to os-block-device-mapping
        # extension after refactor it.
        if block_device_mapping:
            context.can(server_policies.SERVERS % 'create:attach_volume',
                        target)

        image_uuid = self._image_from_req_data(server_dict, create_kwargs)

        # NOTE(cyeoh): Although an extension can set
        # return_reservation_id in order to request that a reservation
        # id be returned to the client instead of the newly created
        # instance information we do not want to pass this parameter
        # to the compute create call which always returns both. We use
        # this flag after the instance create call to determine what
        # to return to the client
        return_reservation_id = create_kwargs.pop('return_reservation_id',
                                                  False)

        requested_networks = None
        if ('os-networks' in self.extension_info.get_extensions()
                or utils.is_neutron()):
            requested_networks = server_dict.get('networks')

        if requested_networks is not None:
            requested_networks = self._get_requested_networks(
                requested_networks)

        if requested_networks and len(requested_networks):
            context.can(server_policies.SERVERS % 'create:attach_network',
                        target)

        try:
            flavor_id = self._flavor_id_from_req_data(body)
        except ValueError:
            msg = _("Invalid flavorRef provided.")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            inst_type = flavors.get_flavor_by_flavor_id(
                    flavor_id, ctxt=context, read_deleted="no")

            (instances, resv_id) = self.compute_api.create(context,
                            inst_type,
                            image_uuid,
                            display_name=name,
                            display_description=description,
                            availability_zone=availability_zone,
                            forced_host=host, forced_node=node,
                            metadata=server_dict.get('metadata', {}),
                            admin_password=password,
                            requested_networks=requested_networks,
                            check_server_group_quota=True,
                            **create_kwargs)
        except (exception.QuotaError,
                exception.PortLimitExceeded) as error:
            raise exc.HTTPForbidden(
                explanation=error.format_message())
        except exception.ImageNotFound:
            msg = _("Can not find requested image")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.FlavorNotFound:
            msg = _("Invalid flavorRef provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.KeypairNotFound:
            msg = _("Invalid key_name provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.ConfigDriveInvalidValue:
            msg = _("Invalid config_drive provided.")
            raise exc.HTTPBadRequest(explanation=msg)
        except exception.ExternalNetworkAttachForbidden as error:
            raise exc.HTTPForbidden(explanation=error.format_message())
        except messaging.RemoteError as err:
            msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
                                                 'err_msg': err.value}
            raise exc.HTTPBadRequest(explanation=msg)
        except UnicodeDecodeError as error:
            msg = "UnicodeError: %s" % error
            raise exc.HTTPBadRequest(explanation=msg)
        except (exception.ImageNotActive,
                exception.ImageBadRequest,
                exception.FixedIpNotFoundForAddress,
                exception.FlavorDiskTooSmall,
                exception.FlavorMemoryTooSmall,
                exception.InvalidMetadata,
                exception.InvalidRequest,
                exception.InvalidVolume,
                exception.MultiplePortsNotApplicable,
                exception.InvalidFixedIpAndMaxCountRequest,
                exception.InstanceUserDataMalformed,
                exception.InstanceUserDataTooLarge,
                exception.PortNotFound,
                exception.FixedIpAlreadyInUse,
                exception.SecurityGroupNotFound,
                exception.PortRequiresFixedIP,
                exception.NetworkRequiresSubnet,
                exception.NetworkNotFound,
                exception.NetworkDuplicated,
                exception.InvalidBDM,
                exception.InvalidBDMSnapshot,
                exception.InvalidBDMVolume,
                exception.InvalidBDMImage,
                exception.InvalidBDMBootSequence,
                exception.InvalidBDMLocalsLimit,
                exception.InvalidBDMVolumeNotBootable,
                exception.InvalidBDMEphemeralSize,
                exception.InvalidBDMFormat,
                exception.InvalidBDMSwapSize,
                exception.AutoDiskConfigDisabledByImage,
                exception.ImageNUMATopologyIncomplete,
                exception.ImageNUMATopologyForbidden,
                exception.ImageNUMATopologyAsymmetric,
                exception.ImageNUMATopologyCPUOutOfRange,
                exception.ImageNUMATopologyCPUDuplicates,
                exception.ImageNUMATopologyCPUsUnassigned,
                exception.ImageNUMATopologyMemoryOutOfRange,
                exception.InstanceGroupNotFound,
                exception.PciRequestAliasNotDefined,
                exception.UnableToAutoAllocateNetwork) as error:
            raise exc.HTTPBadRequest(explanation=error.format_message())
        except (exception.PortInUse,
                exception.InstanceExists,
                exception.NetworkAmbiguous,
                exception.NoUniqueMatch) as error:
            raise exc.HTTPConflict(explanation=error.format_message())

        # If the caller wanted a reservation_id, return it
        if return_reservation_id:
            # NOTE(cyeoh): In v3 reservation_id was wrapped in
            # servers_reservation but this is reverted for V2 API
            # compatibility. In the long term with the tasks API we
            # will probably just drop the concept of reservation_id
            return wsgi.ResponseObject({'reservation_id': resv_id})

        req.cache_db_instances(instances)
        server = self._view_builder.create(req, instances[0])

        if CONF.enable_instance_password:
            server['server']['adminPass'] = password

        robj = wsgi.ResponseObject(server)

        return self._add_location(robj)
Example #52
0
    def _evacuate(self, req, id, body):
        """Permit admins to evacuate a server from a failed host
        to a new one.
        """
        context = req.environ["nova.context"]
        instance = common.get_instance(self.compute_api, context, id)
        context.can(evac_policies.BASE_POLICY_NAME,
                    target={'user_id': instance.user_id,
                            'project_id': instance.project_id})

        evacuate_body = body["evacuate"]
        host = evacuate_body.get("host")
        force = None

        on_shared_storage = self._get_on_shared_storage(req, evacuate_body)

        if api_version_request.is_supported(req, min_version='2.29'):
            force = body["evacuate"].get("force", False)
            force = strutils.bool_from_string(force, strict=True)
            if force is True and not host:
                message = _("Can't force to a non-provided destination")
                raise exc.HTTPBadRequest(explanation=message)
        if api_version_request.is_supported(req, min_version='2.14'):
            password = self._get_password_v214(req, evacuate_body)
        else:
            password = self._get_password(req, evacuate_body,
                                          on_shared_storage)

        if host is not None:
            try:
                self.host_api.service_get_by_compute_host(context, host)
            except (exception.ComputeHostNotFound,
                    exception.HostMappingNotFound):
                msg = _("Compute host %s not found.") % host
                raise exc.HTTPNotFound(explanation=msg)

        if instance.host == host:
            msg = _("The target host can't be the same one.")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            self.compute_api.evacuate(context, instance, host,
                                      on_shared_storage, password, force)
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'evacuate', id)
        except exception.ComputeServiceInUse as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.ForbiddenWithAccelerators as e:
            raise exc.HTTPForbidden(explanation=e.format_message())
        except (
            exception.OperationNotSupportedForVTPM,
            exception.OperationNotSupportedForVDPAInterface,
        ) as e:
            raise exc.HTTPConflict(explanation=e.format_message())

        if (not api_version_request.is_supported(req, min_version='2.14') and
                CONF.api.enable_instance_password):
            return {'adminPass': password}
        else:
            return None
Example #53
0
    def _view_hypervisor(
        self, hypervisor, service, detail, req, servers=None,
        with_servers=False,
    ):
        alive = self.servicegroup_api.service_is_up(service)
        # The 2.53 microversion returns the compute node uuid rather than id.
        uuid_for_id = api_version_request.is_supported(
            req, min_version=UUID_FOR_ID_MIN_VERSION)

        hyp_dict = {
            'id': hypervisor.uuid if uuid_for_id else hypervisor.id,
            'hypervisor_hostname': hypervisor.hypervisor_hostname,
            'state': 'up' if alive else 'down',
            'status': 'disabled' if service.disabled else 'enabled',
        }

        if detail:
            for field in (
                'hypervisor_type', 'hypervisor_version', 'host_ip',
            ):
                hyp_dict[field] = getattr(hypervisor, field)

            hyp_dict['service'] = {
                'id': service.uuid if uuid_for_id else service.id,
                'host': hypervisor.host,
                'disabled_reason': service.disabled_reason,
            }

        # The 2.88 microversion removed these fields, so only add them on older
        # microversions
        if detail and api_version_request.is_supported(
            req, max_version='2.87',
        ):
            for field in (
                'vcpus', 'memory_mb', 'local_gb', 'vcpus_used',
                'memory_mb_used', 'local_gb_used', 'free_ram_mb',
                'free_disk_gb', 'current_workload', 'running_vms',
                'disk_available_least',
            ):
                hyp_dict[field] = getattr(hypervisor, field)

            if api_version_request.is_supported(req, max_version='2.27'):
                hyp_dict['cpu_info'] = hypervisor.cpu_info
            else:
                if hypervisor.cpu_info:
                    hyp_dict['cpu_info'] = jsonutils.loads(hypervisor.cpu_info)
                else:
                    hyp_dict['cpu_info'] = {}

        # The 2.88 microversion also *added* the 'uptime' field to the response
        if detail and api_version_request.is_supported(
            req, min_version='2.88',
        ):
            try:
                hyp_dict['uptime'] = self.host_api.get_host_uptime(
                    req.environ['nova.context'], hypervisor.host)
            except (
                NotImplementedError,
                exception.ComputeServiceUnavailable,
                exception.HostMappingNotFound,
                exception.HostNotFound,
            ):
                # Not all virt drivers support this, and it's not generally
                # possible to get uptime for a down host
                hyp_dict['uptime'] = None

        if servers:
            hyp_dict['servers'] = [
                {'name': serv['name'], 'uuid': serv['uuid']}
                for serv in servers
            ]
        # The 2.75 microversion adds 'servers' field always in response.
        # Empty list if there are no servers on hypervisors and it is
        # requested in request.
        elif with_servers and api_version_request.is_supported(
            req, min_version='2.75',
        ):
            hyp_dict['servers'] = []

        return hyp_dict