Add flavor access management support
Change-Id: I1ffd0cbcc4b34393fd0186ccc17496c6adf1ee93
This commit is contained in:
parent
6fe3d0d43d
commit
e0ca68c562
98
api-ref/source/v1/flavor_access.inc
Normal file
98
api-ref/source/v1/flavor_access.inc
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
.. -*- rst -*-
|
||||||
|
|
||||||
|
================
|
||||||
|
Flavors access
|
||||||
|
================
|
||||||
|
|
||||||
|
Lists tenants who have access to a private flavor and adds private
|
||||||
|
flavor access to and removes private flavor access from tenants. By
|
||||||
|
default, only administrators can manage private flavor access. A private
|
||||||
|
flavor has ``is_public`` set to ``false`` while a public flavor has
|
||||||
|
``is_public`` set to ``true``.
|
||||||
|
|
||||||
|
List Flavor Access Information For Given Flavor
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
.. rest_method:: GET /flavors/{flavor_uuid}/access
|
||||||
|
|
||||||
|
Lists flavor access information.
|
||||||
|
|
||||||
|
Normal response codes: 200
|
||||||
|
|
||||||
|
Error response codes: unauthorized(401), forbidden(403)
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- flavor_uuid: flavor_uuid_path
|
||||||
|
|
||||||
|
Response
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- flavor_access: flavor_access
|
||||||
|
|
||||||
|
**Example List Flavor Access Information For Given Flavor: JSON response**
|
||||||
|
|
||||||
|
.. literalinclude:: samples/flavor_access/flavor-access-list-resp.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Add Flavor Access To Tenant
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. rest_method:: POST /flavors/{flavor_uuid}/access
|
||||||
|
|
||||||
|
Adds flavor access to a tenant and flavor.
|
||||||
|
|
||||||
|
Specify the ``tenant_id`` in the request body.
|
||||||
|
|
||||||
|
Normal response codes: 204
|
||||||
|
|
||||||
|
Error response codes: badRequest(400), unauthorized(401),
|
||||||
|
forbidden(403), conflict(409)
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- flavor_uuid: flavor_uuid_path
|
||||||
|
- tenant_id: tenant_id_body
|
||||||
|
|
||||||
|
**Example Add Flavor Access To Tenant: JSON response**
|
||||||
|
|
||||||
|
.. literalinclude:: samples/flavor_access/flavor-access-add-tenant-req.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Response
|
||||||
|
--------
|
||||||
|
|
||||||
|
If successful, this method does not return content in the response body.
|
||||||
|
|
||||||
|
Remove Flavor Access From Tenant
|
||||||
|
================================
|
||||||
|
|
||||||
|
.. rest_method:: DELETE /flavors/{flavor_uuid}/access/{tenant_id}
|
||||||
|
|
||||||
|
Removes flavor access from a tenant and flavor.
|
||||||
|
|
||||||
|
Normal response codes: 204
|
||||||
|
|
||||||
|
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
|
||||||
|
itemNotFound(404), conflict(409)
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- flavor_uuid: flavor_uuid_path
|
||||||
|
- tenant_id: tenant_id_path
|
||||||
|
|
||||||
|
Response
|
||||||
|
--------
|
||||||
|
|
||||||
|
If successful, this method does not return content in the response body.
|
@ -11,4 +11,5 @@ Baremetal Compute API V1 (CURRENT)
|
|||||||
.. include:: instance_states.inc
|
.. include:: instance_states.inc
|
||||||
.. include:: instance_networks.inc
|
.. include:: instance_networks.inc
|
||||||
.. include:: flavors.inc
|
.. include:: flavors.inc
|
||||||
|
.. include:: flavor_access.inc
|
||||||
.. include:: availability_zones.inc
|
.. include:: availability_zones.inc
|
||||||
|
@ -39,6 +39,12 @@ spec_key_path:
|
|||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
tenant_id_path:
|
||||||
|
description: |
|
||||||
|
The UUID of the tenant in a multi-tenancy cloud.
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
# variables in query
|
# variables in query
|
||||||
all_tenants:
|
all_tenants:
|
||||||
@ -125,6 +131,12 @@ fixed_address:
|
|||||||
in: body
|
in: body
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
|
flavor_access:
|
||||||
|
description: |
|
||||||
|
A list of tenants.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: array
|
||||||
flavor_description:
|
flavor_description:
|
||||||
description: |
|
description: |
|
||||||
The description of the flavor.
|
The description of the flavor.
|
||||||
@ -402,6 +414,12 @@ provision_state:
|
|||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
tenant_id_body:
|
||||||
|
description: |
|
||||||
|
The UUID of the tenant in a multi-tenancy cloud.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
updated_at:
|
updated_at:
|
||||||
description: |
|
description: |
|
||||||
The date and time when the resource was updated. The date and time
|
The date and time when the resource was updated. The date and time
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"tenant_id": "fake_tenant"
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"flavor_access": [
|
||||||
|
"tenant1",
|
||||||
|
"tenant2",
|
||||||
|
"tenant3"
|
||||||
|
]
|
||||||
|
}
|
@ -21,13 +21,23 @@ from wsme import types as wtypes
|
|||||||
|
|
||||||
from mogan.api.controllers import base
|
from mogan.api.controllers import base
|
||||||
from mogan.api.controllers import link
|
from mogan.api.controllers import link
|
||||||
|
from mogan.api.controllers.v1.schemas import flavor_access
|
||||||
from mogan.api.controllers.v1 import types
|
from mogan.api.controllers.v1 import types
|
||||||
from mogan.api import expose
|
from mogan.api import expose
|
||||||
|
from mogan.api import validation
|
||||||
from mogan.common import exception
|
from mogan.common import exception
|
||||||
from mogan.common.i18n import _
|
from mogan.common.i18n import _
|
||||||
from mogan import objects
|
from mogan import objects
|
||||||
|
|
||||||
|
|
||||||
|
def _marshall_flavor_access(flavor):
|
||||||
|
rval = []
|
||||||
|
for project_id in flavor.projects:
|
||||||
|
rval.append(project_id)
|
||||||
|
|
||||||
|
return {'flavor_access': rval}
|
||||||
|
|
||||||
|
|
||||||
class Flavor(base.APIBase):
|
class Flavor(base.APIBase):
|
||||||
"""API representation of a flavor.
|
"""API representation of a flavor.
|
||||||
|
|
||||||
@ -125,10 +135,74 @@ class FlavorExtraSpecsController(rest.RestController):
|
|||||||
flavor.save()
|
flavor.save()
|
||||||
|
|
||||||
|
|
||||||
|
class FlavorAccessController(rest.RestController):
|
||||||
|
"""REST controller for flavor access."""
|
||||||
|
|
||||||
|
@expose.expose(wtypes.text, types.uuid)
|
||||||
|
def get_all(self, flavor_uuid):
|
||||||
|
"""Retrieve a list of extra specs of the queried flavor."""
|
||||||
|
|
||||||
|
flavor = objects.InstanceType.get(pecan.request.context,
|
||||||
|
flavor_uuid)
|
||||||
|
|
||||||
|
# public flavor to all projects
|
||||||
|
if flavor.is_public:
|
||||||
|
msg = _("Access list not available for public flavors.")
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
msg, status_code=http_client.NOT_FOUND)
|
||||||
|
|
||||||
|
# private flavor to listed projects only
|
||||||
|
return _marshall_flavor_access(flavor)
|
||||||
|
|
||||||
|
@expose.expose(None, types.uuid, body=types.jsontype,
|
||||||
|
status_code=http_client.NO_CONTENT)
|
||||||
|
def post(self, flavor_uuid, tenant):
|
||||||
|
"""Add flavor access for the given tenant."""
|
||||||
|
validation.check_schema(tenant, flavor_access.add_tenant_access)
|
||||||
|
|
||||||
|
flavor = objects.InstanceType.get(pecan.request.context,
|
||||||
|
flavor_uuid)
|
||||||
|
if flavor.is_public:
|
||||||
|
msg = _("Can not add access to a public flavor.")
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
msg, status_code=http_client.CONFLICT)
|
||||||
|
|
||||||
|
try:
|
||||||
|
flavor.projects.append(tenant['tenant_id'])
|
||||||
|
flavor.save()
|
||||||
|
except exception.FlavorNotFound as e:
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
e.message, status_code=http_client.NOT_FOUND)
|
||||||
|
except exception.FlavorAccessExists as err:
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
err.message, status_code=http_client.CONFLICT)
|
||||||
|
|
||||||
|
@expose.expose(None, types.uuid, types.uuid,
|
||||||
|
status_code=http_client.NO_CONTENT)
|
||||||
|
def delete(self, flavor_uuid, tenant_id):
|
||||||
|
"""Remove flavor access for the given tenant."""
|
||||||
|
|
||||||
|
flavor = objects.InstanceType.get(pecan.request.context,
|
||||||
|
flavor_uuid)
|
||||||
|
try:
|
||||||
|
# TODO(zhenguo): this should be synchronized.
|
||||||
|
if tenant_id in flavor.projects:
|
||||||
|
flavor.projects.remove(tenant_id)
|
||||||
|
flavor.save()
|
||||||
|
else:
|
||||||
|
raise exception.FlavorAccessNotFound(flavor_id=flavor.uuid,
|
||||||
|
project_id=tenant_id)
|
||||||
|
except (exception.FlavorAccessNotFound,
|
||||||
|
exception.FlavorNotFound) as e:
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
e.message, status_code=http_client.NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
class FlavorsController(rest.RestController):
|
class FlavorsController(rest.RestController):
|
||||||
"""REST controller for Flavors."""
|
"""REST controller for Flavors."""
|
||||||
|
|
||||||
extraspecs = FlavorExtraSpecsController()
|
extraspecs = FlavorExtraSpecsController()
|
||||||
|
access = FlavorAccessController()
|
||||||
|
|
||||||
@expose.expose(FlavorCollection)
|
@expose.expose(FlavorCollection)
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
|
26
mogan/api/controllers/v1/schemas/flavor_access.py
Normal file
26
mogan/api/controllers/v1/schemas/flavor_access.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright 2017 Huawei Technologies Co.,LTD.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
add_tenant_access = {
|
||||||
|
'type': 'object',
|
||||||
|
'properties': {
|
||||||
|
'tenant_id': {
|
||||||
|
'type': 'string', 'format': 'uuid',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'required': ['tenant_id'],
|
||||||
|
'additionalProperties': False,
|
||||||
|
}
|
@ -164,6 +164,16 @@ class InstanceNotFound(NotFound):
|
|||||||
_msg_fmt = _("Instance %(instance)s could not be found.")
|
_msg_fmt = _("Instance %(instance)s could not be found.")
|
||||||
|
|
||||||
|
|
||||||
|
class FlavorAccessExists(MoganException):
|
||||||
|
_msg_fmt = _("Flavor access already exists for flavor %(flavor_id)s "
|
||||||
|
"and project %(project_id)s combination.")
|
||||||
|
|
||||||
|
|
||||||
|
class FlavorAccessNotFound(NotFound):
|
||||||
|
_msg_fmt = _("Flavor access not found for %(flavor_id)s / "
|
||||||
|
"%(project_id)s combination.")
|
||||||
|
|
||||||
|
|
||||||
class ComputeNodeAlreadyExists(MoganException):
|
class ComputeNodeAlreadyExists(MoganException):
|
||||||
_msg_fmt = _("ComputeNode with node_uuid %(node)s already exists.")
|
_msg_fmt = _("ComputeNode with node_uuid %(node)s already exists.")
|
||||||
|
|
||||||
|
@ -179,6 +179,19 @@ class Connection(object):
|
|||||||
extra specs dict argument
|
extra specs dict argument
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Flavor access
|
||||||
|
@abc.abstractmethod
|
||||||
|
def flavor_access_add(self, context, flavor_id, project_id):
|
||||||
|
"""Add flavor access for project."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def flavor_access_get(self, context, flavor_id):
|
||||||
|
"""Get flavor access by flavor id."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def flavor_access_remove(self, context, flavor_id, project_id):
|
||||||
|
"""Remove flavor access for project."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def instance_nics_get_by_instance_uuid(self, context, instance_uuid):
|
def instance_nics_get_by_instance_uuid(self, context, instance_uuid):
|
||||||
"""Get the Nics info of an instnace.
|
"""Get the Nics info of an instnace.
|
||||||
|
@ -164,6 +164,13 @@ class Connection(api.Connection):
|
|||||||
instance_type_uuid=type_id)
|
instance_type_uuid=type_id)
|
||||||
extra_query.delete()
|
extra_query.delete()
|
||||||
|
|
||||||
|
# Clean up all access related to this flavor
|
||||||
|
project_query = model_query(
|
||||||
|
context,
|
||||||
|
models.InstanceTypeProjects).filter_by(
|
||||||
|
instance_type_uuid=type_id)
|
||||||
|
project_query.delete()
|
||||||
|
|
||||||
# Then delete the type record
|
# Then delete the type record
|
||||||
query = model_query(context, models.InstanceTypes)
|
query = model_query(context, models.InstanceTypes)
|
||||||
query = add_identity_filter(query, instance_type_uuid)
|
query = add_identity_filter(query, instance_type_uuid)
|
||||||
@ -470,6 +477,31 @@ class Connection(api.Connection):
|
|||||||
raise exception.FlavorExtraSpecsNotFound(
|
raise exception.FlavorExtraSpecsNotFound(
|
||||||
extra_specs_key=key, flavor_id=type_id)
|
extra_specs_key=key, flavor_id=type_id)
|
||||||
|
|
||||||
|
def flavor_access_get(self, context, flavor_id):
|
||||||
|
return _flavor_access_query(context, flavor_id)
|
||||||
|
|
||||||
|
def flavor_access_add(self, context, flavor_id, project_id):
|
||||||
|
access_ref = models.InstanceTypeProjects()
|
||||||
|
access_ref.update({"instance_type_uuid": flavor_id,
|
||||||
|
"project_id": project_id})
|
||||||
|
with _session_for_write() as session:
|
||||||
|
try:
|
||||||
|
session.add(access_ref)
|
||||||
|
session.flush()
|
||||||
|
except db_exc.DBDuplicateEntry:
|
||||||
|
raise exception.FlavorAccessExists(flavor_id=flavor_id,
|
||||||
|
project_id=project_id)
|
||||||
|
return access_ref
|
||||||
|
|
||||||
|
def flavor_access_remove(self, context, flavor_id, project_id):
|
||||||
|
count = _flavor_access_query(context, flavor_id). \
|
||||||
|
filter_by(project_id=project_id). \
|
||||||
|
delete(synchronize_session=False)
|
||||||
|
|
||||||
|
if count == 0:
|
||||||
|
raise exception.FlavorAccessNotFound(flavor_id=flavor_id,
|
||||||
|
project_id=project_id)
|
||||||
|
|
||||||
def instance_nic_update_or_create(self, context, port_id, values):
|
def instance_nic_update_or_create(self, context, port_id, values):
|
||||||
with _session_for_write() as session:
|
with _session_for_write() as session:
|
||||||
query = model_query(context, models.InstanceNic).filter_by(
|
query = model_query(context, models.InstanceNic).filter_by(
|
||||||
@ -884,3 +916,8 @@ def _type_get_id_from_type(context, type_id):
|
|||||||
def _type_extra_specs_get_query(context, type_id):
|
def _type_extra_specs_get_query(context, type_id):
|
||||||
return model_query(context, models.InstanceTypeExtraSpecs). \
|
return model_query(context, models.InstanceTypeExtraSpecs). \
|
||||||
filter_by(instance_type_uuid=type_id)
|
filter_by(instance_type_uuid=type_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _flavor_access_query(context, flavor_id):
|
||||||
|
return model_query(context, models.InstanceTypeProjects). \
|
||||||
|
filter_by(instance_type_uuid=flavor_id)
|
||||||
|
@ -21,6 +21,9 @@ from mogan.objects import base
|
|||||||
from mogan.objects import fields as object_fields
|
from mogan.objects import fields as object_fields
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONAL_FIELDS = ['extra_specs', 'projects']
|
||||||
|
|
||||||
|
|
||||||
@base.MoganObjectRegistry.register
|
@base.MoganObjectRegistry.register
|
||||||
class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
||||||
# Version 1.0: Initial version
|
# Version 1.0: Initial version
|
||||||
@ -34,11 +37,40 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
'description': object_fields.StringField(nullable=True),
|
'description': object_fields.StringField(nullable=True),
|
||||||
'is_public': object_fields.BooleanField(),
|
'is_public': object_fields.BooleanField(),
|
||||||
'extra_specs': object_fields.FlexibleDictField(),
|
'extra_specs': object_fields.FlexibleDictField(),
|
||||||
|
'projects': object_fields.ListOfStringsField(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(InstanceType, self).__init__(*args, **kwargs)
|
super(InstanceType, self).__init__(*args, **kwargs)
|
||||||
self._orig_extra_specs = {}
|
self._orig_extra_specs = {}
|
||||||
|
self._orig_projects = {}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _from_db_object(context, flavor, db_flavor, expected_attrs=None):
|
||||||
|
if expected_attrs is None:
|
||||||
|
expected_attrs = []
|
||||||
|
|
||||||
|
for name, field in flavor.fields.items():
|
||||||
|
if name in OPTIONAL_FIELDS:
|
||||||
|
continue
|
||||||
|
value = db_flavor[name]
|
||||||
|
if isinstance(field, object_fields.IntegerField):
|
||||||
|
value = value if value is not None else 0
|
||||||
|
flavor[name] = value
|
||||||
|
|
||||||
|
if 'extra_specs' in expected_attrs:
|
||||||
|
flavor.extra_specs = db_flavor['extra_specs']
|
||||||
|
|
||||||
|
if 'projects' in expected_attrs:
|
||||||
|
flavor._load_projects(context)
|
||||||
|
|
||||||
|
flavor.obj_reset_changes()
|
||||||
|
return flavor
|
||||||
|
|
||||||
|
def _load_projects(self, context):
|
||||||
|
self.projects = [x['project_id'] for x in
|
||||||
|
self.dbapi.flavor_access_get(context, self.uuid)]
|
||||||
|
self.obj_reset_changes(['projects'])
|
||||||
|
|
||||||
def obj_reset_changes(self, fields=None, recursive=False):
|
def obj_reset_changes(self, fields=None, recursive=False):
|
||||||
super(InstanceType, self).obj_reset_changes(fields=fields,
|
super(InstanceType, self).obj_reset_changes(fields=fields,
|
||||||
@ -47,18 +79,25 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
self._orig_extra_specs = (dict(self.extra_specs)
|
self._orig_extra_specs = (dict(self.extra_specs)
|
||||||
if self.obj_attr_is_set('extra_specs')
|
if self.obj_attr_is_set('extra_specs')
|
||||||
else {})
|
else {})
|
||||||
|
if fields is None or 'projects' in fields:
|
||||||
|
self._orig_projects = (list(self.projects)
|
||||||
|
if self.obj_attr_is_set('projects')
|
||||||
|
else [])
|
||||||
|
|
||||||
def obj_what_changed(self):
|
def obj_what_changed(self):
|
||||||
changes = super(InstanceType, self).obj_what_changed()
|
changes = super(InstanceType, self).obj_what_changed()
|
||||||
if ('extra_specs' in self and
|
if ('extra_specs' in self and
|
||||||
self.extra_specs != self._orig_extra_specs):
|
self.extra_specs != self._orig_extra_specs):
|
||||||
changes.add('extra_specs')
|
changes.add('extra_specs')
|
||||||
|
if 'projects' in self and self.projects != self._orig_projects:
|
||||||
|
changes.add('projects')
|
||||||
return changes
|
return changes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _from_db_object_list(db_objects, cls, context):
|
def _from_db_object_list(db_objects, cls, context):
|
||||||
"""Converts a list of database entities to a list of formal objects."""
|
"""Converts a list of database entities to a list of formal objects."""
|
||||||
return [InstanceType._from_db_object(context, cls(context), obj)
|
return [InstanceType._from_db_object(context, cls(context), obj,
|
||||||
|
expected_attrs=['extra_specs'])
|
||||||
for obj in db_objects]
|
for obj in db_objects]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -73,15 +112,17 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
"""Find a Instance Type and return a Instance Type object."""
|
"""Find a Instance Type and return a Instance Type object."""
|
||||||
db_instance_type = cls.dbapi.instance_type_get(context,
|
db_instance_type = cls.dbapi.instance_type_get(context,
|
||||||
instance_type_uuid)
|
instance_type_uuid)
|
||||||
instance_type = InstanceType._from_db_object(context, cls(context),
|
instance_type = InstanceType._from_db_object(
|
||||||
db_instance_type)
|
context, cls(context), db_instance_type,
|
||||||
|
expected_attrs=['extra_specs', 'projects'])
|
||||||
return instance_type
|
return instance_type
|
||||||
|
|
||||||
def create(self, context=None):
|
def create(self, context=None):
|
||||||
"""Create a Instance Type record in the DB."""
|
"""Create a Instance Type record in the DB."""
|
||||||
values = self.obj_get_changes()
|
values = self.obj_get_changes()
|
||||||
db_instance_type = self.dbapi.instance_type_create(context, values)
|
db_instance_type = self.dbapi.instance_type_create(context, values)
|
||||||
self._from_db_object(context, self, db_instance_type)
|
self._from_db_object(context, self, db_instance_type,
|
||||||
|
expected_attrs=['extra_specs'])
|
||||||
|
|
||||||
def destroy(self, context=None):
|
def destroy(self, context=None):
|
||||||
"""Delete the Instance Type from the DB."""
|
"""Delete the Instance Type from the DB."""
|
||||||
@ -90,6 +131,7 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
|
|
||||||
def save(self, context=None):
|
def save(self, context=None):
|
||||||
updates = self.obj_get_changes()
|
updates = self.obj_get_changes()
|
||||||
|
projects = updates.pop('projects', None)
|
||||||
extra_specs = updates.pop('extra_specs', None)
|
extra_specs = updates.pop('extra_specs', None)
|
||||||
if extra_specs is not None:
|
if extra_specs is not None:
|
||||||
deleted_keys = (set(self._orig_extra_specs.keys()) -
|
deleted_keys = (set(self._orig_extra_specs.keys()) -
|
||||||
@ -98,8 +140,18 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
else:
|
else:
|
||||||
added_keys = deleted_keys = None
|
added_keys = deleted_keys = None
|
||||||
|
|
||||||
|
if projects is not None:
|
||||||
|
deleted_projects = set(self._orig_projects) - set(projects)
|
||||||
|
added_projects = set(projects) - set(self._orig_projects)
|
||||||
|
else:
|
||||||
|
added_projects = deleted_projects = None
|
||||||
|
|
||||||
if added_keys or deleted_keys:
|
if added_keys or deleted_keys:
|
||||||
self.save_extra_specs(context, self.extra_specs, deleted_keys)
|
self.save_extra_specs(context, self.extra_specs, deleted_keys)
|
||||||
|
|
||||||
|
if added_projects or deleted_projects:
|
||||||
|
self.save_projects(context, added_projects, deleted_projects)
|
||||||
|
|
||||||
self.dbapi.instance_type_update(context, self.uuid, updates)
|
self.dbapi.instance_type_update(context, self.uuid, updates)
|
||||||
|
|
||||||
def save_extra_specs(self, context, to_add=None, to_delete=None):
|
def save_extra_specs(self, context, to_add=None, to_delete=None):
|
||||||
@ -119,3 +171,21 @@ class InstanceType(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||||||
for key in to_delete:
|
for key in to_delete:
|
||||||
self.dbapi.type_extra_specs_delete(context, ident, key)
|
self.dbapi.type_extra_specs_delete(context, ident, key)
|
||||||
self.obj_reset_changes(['extra_specs'])
|
self.obj_reset_changes(['extra_specs'])
|
||||||
|
|
||||||
|
def save_projects(self, context, to_add=None, to_delete=None):
|
||||||
|
"""Add or delete projects.
|
||||||
|
|
||||||
|
:param:to_add: A list of projects to add
|
||||||
|
:param:to_delete: A list of projects to remove
|
||||||
|
"""
|
||||||
|
ident = self.uuid
|
||||||
|
|
||||||
|
to_add = to_add if to_add is not None else []
|
||||||
|
to_delete = to_delete if to_delete is not None else []
|
||||||
|
|
||||||
|
for project_id in to_add:
|
||||||
|
self.dbapi.flavor_access_add(context, ident, project_id)
|
||||||
|
|
||||||
|
for project_id in to_delete:
|
||||||
|
self.dbapi.flavor_access_remove(context, ident, project_id)
|
||||||
|
self.obj_reset_changes(['projects'])
|
||||||
|
@ -391,7 +391,7 @@ expected_object_fingerprints = {
|
|||||||
'ComputeDiskList': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
'ComputeDiskList': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
||||||
'InstanceFault': '1.0-6b5b01b2cc7b6b547837acb168ec6eb9',
|
'InstanceFault': '1.0-6b5b01b2cc7b6b547837acb168ec6eb9',
|
||||||
'InstanceFaultList': '1.0-43e8aad0258652921f929934e9e048fd',
|
'InstanceFaultList': '1.0-43e8aad0258652921f929934e9e048fd',
|
||||||
'InstanceType': '1.0-589b096651fcdb30898ff50f748dd948',
|
'InstanceType': '1.0-d1cf232312ff8101aa5a19908b476d67',
|
||||||
'MyObj': '1.1-aad62eedc5a5cc8bcaf2982c285e753f',
|
'MyObj': '1.1-aad62eedc5a5cc8bcaf2982c285e753f',
|
||||||
'InstanceNic': '1.0-78744332fe105f9c1796dc5295713d9f',
|
'InstanceNic': '1.0-78744332fe105f9c1796dc5295713d9f',
|
||||||
'InstanceNics': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
'InstanceNics': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
||||||
|
Loading…
Reference in New Issue
Block a user