Move the placement code to the base
While in Nova, placement code was relegated to the 'nova/api/openstack/placement/' directory. This commit moves that directory to the base of the repo, and removes the unnecesary directories. Change-Id: I023f525d064374283456c77edb6ac7b3d0cf0b0c
This commit is contained in:
parent
ae561723ee
commit
fb7c190990
@ -1,223 +0,0 @@
|
||||
# Copyright (c) 2012 Citrix Systems, Inc.
|
||||
# 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.
|
||||
|
||||
"""The Aggregate admin API extension."""
|
||||
|
||||
import datetime
|
||||
|
||||
from webob import exc
|
||||
|
||||
from nova.api.openstack import api_version_request
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack.compute.schemas import aggregates
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api import validation
|
||||
from nova.compute import api as compute_api
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.policies import aggregates as aggr_policies
|
||||
|
||||
|
||||
def _get_context(req):
|
||||
return req.environ['nova.context']
|
||||
|
||||
|
||||
class AggregateController(wsgi.Controller):
|
||||
"""The Host Aggregates API controller for the OpenStack API."""
|
||||
def __init__(self):
|
||||
self.api = compute_api.AggregateAPI()
|
||||
|
||||
@wsgi.expected_errors(())
|
||||
def index(self, req):
|
||||
"""Returns a list a host aggregate's id, name, availability_zone."""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'index')
|
||||
aggregates = self.api.get_aggregate_list(context)
|
||||
return {'aggregates': [self._marshall_aggregate(req, a)['aggregate']
|
||||
for a in aggregates]}
|
||||
|
||||
# NOTE(gmann): Returns 200 for backwards compatibility but should be 201
|
||||
# as this operation complete the creation of aggregates resource.
|
||||
@wsgi.expected_errors((400, 409))
|
||||
@validation.schema(aggregates.create_v20, '2.0', '2.0')
|
||||
@validation.schema(aggregates.create, '2.1')
|
||||
def create(self, req, body):
|
||||
"""Creates an aggregate, given its name and
|
||||
optional availability zone.
|
||||
"""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'create')
|
||||
host_aggregate = body["aggregate"]
|
||||
name = common.normalize_name(host_aggregate["name"])
|
||||
avail_zone = host_aggregate.get("availability_zone")
|
||||
if avail_zone:
|
||||
avail_zone = common.normalize_name(avail_zone)
|
||||
|
||||
try:
|
||||
aggregate = self.api.create_aggregate(context, name, avail_zone)
|
||||
except exception.AggregateNameExists as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
except exception.ObjectActionError:
|
||||
raise exc.HTTPConflict(explanation=_(
|
||||
'Not all aggregates have been migrated to the API database'))
|
||||
except exception.InvalidAggregateAction as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
agg = self._marshall_aggregate(req, aggregate)
|
||||
|
||||
# To maintain the same API result as before the changes for returning
|
||||
# nova objects were made.
|
||||
del agg['aggregate']['hosts']
|
||||
del agg['aggregate']['metadata']
|
||||
|
||||
return agg
|
||||
|
||||
@wsgi.expected_errors(404)
|
||||
def show(self, req, id):
|
||||
"""Shows the details of an aggregate, hosts and metadata included."""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'show')
|
||||
try:
|
||||
aggregate = self.api.get_aggregate(context, id)
|
||||
except exception.AggregateNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
return self._marshall_aggregate(req, aggregate)
|
||||
|
||||
@wsgi.expected_errors((400, 404, 409))
|
||||
@validation.schema(aggregates.update_v20, '2.0', '2.0')
|
||||
@validation.schema(aggregates.update, '2.1')
|
||||
def update(self, req, id, body):
|
||||
"""Updates the name and/or availability_zone of given aggregate."""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'update')
|
||||
updates = body["aggregate"]
|
||||
if 'name' in updates:
|
||||
updates['name'] = common.normalize_name(updates['name'])
|
||||
|
||||
try:
|
||||
aggregate = self.api.update_aggregate(context, id, updates)
|
||||
except exception.AggregateNameExists as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
except exception.AggregateNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.InvalidAggregateAction as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
return self._marshall_aggregate(req, aggregate)
|
||||
|
||||
# NOTE(gmann): Returns 200 for backwards compatibility but should be 204
|
||||
# as this operation complete the deletion of aggregate resource and return
|
||||
# no response body.
|
||||
@wsgi.expected_errors((400, 404))
|
||||
def delete(self, req, id):
|
||||
"""Removes an aggregate by id."""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'delete')
|
||||
try:
|
||||
self.api.delete_aggregate(context, id)
|
||||
except exception.AggregateNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.InvalidAggregateAction as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
# NOTE(gmann): Returns 200 for backwards compatibility but should be 202
|
||||
# for representing async API as this API just accepts the request and
|
||||
# request hypervisor driver to complete the same in async mode.
|
||||
@wsgi.expected_errors((404, 409))
|
||||
@wsgi.action('add_host')
|
||||
@validation.schema(aggregates.add_host)
|
||||
def _add_host(self, req, id, body):
|
||||
"""Adds a host to the specified aggregate."""
|
||||
host = body['add_host']['host']
|
||||
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'add_host')
|
||||
try:
|
||||
aggregate = self.api.add_host_to_aggregate(context, id, host)
|
||||
except (exception.AggregateNotFound,
|
||||
exception.HostMappingNotFound,
|
||||
exception.ComputeHostNotFound) as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except (exception.AggregateHostExists,
|
||||
exception.InvalidAggregateAction) as e:
|
||||
raise exc.HTTPConflict(explanation=e.format_message())
|
||||
return self._marshall_aggregate(req, aggregate)
|
||||
|
||||
# NOTE(gmann): Returns 200 for backwards compatibility but should be 202
|
||||
# for representing async API as this API just accepts the request and
|
||||
# request hypervisor driver to complete the same in async mode.
|
||||
@wsgi.expected_errors((404, 409))
|
||||
@wsgi.action('remove_host')
|
||||
@validation.schema(aggregates.remove_host)
|
||||
def _remove_host(self, req, id, body):
|
||||
"""Removes a host from the specified aggregate."""
|
||||
host = body['remove_host']['host']
|
||||
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'remove_host')
|
||||
try:
|
||||
aggregate = self.api.remove_host_from_aggregate(context, id, host)
|
||||
except (exception.AggregateNotFound, exception.AggregateHostNotFound,
|
||||
exception.HostMappingNotFound, exception.ComputeHostNotFound):
|
||||
msg = _('Cannot remove host %(host)s in aggregate %(id)s') % {
|
||||
'host': host, 'id': id}
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
except exception.InvalidAggregateAction:
|
||||
msg = _('Cannot remove host %(host)s in aggregate %(id)s') % {
|
||||
'host': host, 'id': id}
|
||||
raise exc.HTTPConflict(explanation=msg)
|
||||
return self._marshall_aggregate(req, aggregate)
|
||||
|
||||
@wsgi.expected_errors((400, 404))
|
||||
@wsgi.action('set_metadata')
|
||||
@validation.schema(aggregates.set_metadata)
|
||||
def _set_metadata(self, req, id, body):
|
||||
"""Replaces the aggregate's existing metadata with new metadata."""
|
||||
context = _get_context(req)
|
||||
context.can(aggr_policies.POLICY_ROOT % 'set_metadata')
|
||||
|
||||
metadata = body["set_metadata"]["metadata"]
|
||||
try:
|
||||
aggregate = self.api.update_aggregate_metadata(context,
|
||||
id, metadata)
|
||||
except exception.AggregateNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
except exception.InvalidAggregateAction as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.format_message())
|
||||
|
||||
return self._marshall_aggregate(req, aggregate)
|
||||
|
||||
def _marshall_aggregate(self, req, aggregate):
|
||||
_aggregate = {}
|
||||
for key, value in self._build_aggregate_items(req, aggregate):
|
||||
# NOTE(danms): The original API specified non-TZ-aware timestamps
|
||||
if isinstance(value, datetime.datetime):
|
||||
value = value.replace(tzinfo=None)
|
||||
_aggregate[key] = value
|
||||
return {"aggregate": _aggregate}
|
||||
|
||||
def _build_aggregate_items(self, req, aggregate):
|
||||
show_uuid = api_version_request.is_supported(req, min_version="2.41")
|
||||
keys = aggregate.obj_fields
|
||||
# NOTE(rlrossit): Within the compute API, metadata will always be
|
||||
# set on the aggregate object (at a minimum to {}). Because of this,
|
||||
# we can freely use getattr() on keys in obj_extra_fields (in this
|
||||
# case it is only ['availability_zone']) without worrying about
|
||||
# lazy-loading an unset variable
|
||||
for key in keys:
|
||||
if ((aggregate.obj_attr_is_set(key)
|
||||
or key in aggregate.obj_extra_fields) and
|
||||
(show_uuid or key != 'uuid')):
|
||||
yield key, getattr(aggregate, key)
|
@ -1,131 +0,0 @@
|
||||
# Copyright 2014 NEC Corporation. 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.
|
||||
|
||||
import copy
|
||||
|
||||
from nova.api.validation import parameter_types
|
||||
|
||||
availability_zone = {'oneOf': [parameter_types.az_name, {'type': 'null'}]}
|
||||
availability_zone_with_leading_trailing_spaces = {
|
||||
'oneOf': [parameter_types.az_name_with_leading_trailing_spaces,
|
||||
{'type': 'null'}]
|
||||
}
|
||||
|
||||
|
||||
create = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'aggregate': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': parameter_types.name,
|
||||
'availability_zone': availability_zone,
|
||||
},
|
||||
'required': ['name'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['aggregate'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
|
||||
create_v20 = copy.deepcopy(create)
|
||||
create_v20['properties']['aggregate']['properties']['name'] = (parameter_types.
|
||||
name_with_leading_trailing_spaces)
|
||||
create_v20['properties']['aggregate']['properties']['availability_zone'] = (
|
||||
availability_zone_with_leading_trailing_spaces)
|
||||
|
||||
|
||||
update = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'aggregate': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': parameter_types.name_with_leading_trailing_spaces,
|
||||
'availability_zone': availability_zone
|
||||
},
|
||||
'additionalProperties': False,
|
||||
'anyOf': [
|
||||
{'required': ['name']},
|
||||
{'required': ['availability_zone']}
|
||||
]
|
||||
},
|
||||
},
|
||||
'required': ['aggregate'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
|
||||
update_v20 = copy.deepcopy(update)
|
||||
update_v20['properties']['aggregate']['properties']['name'] = (parameter_types.
|
||||
name_with_leading_trailing_spaces)
|
||||
update_v20['properties']['aggregate']['properties']['availability_zone'] = (
|
||||
availability_zone_with_leading_trailing_spaces)
|
||||
|
||||
|
||||
add_host = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'add_host': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'host': parameter_types.hostname,
|
||||
},
|
||||
'required': ['host'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['add_host'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
remove_host = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'remove_host': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'host': parameter_types.hostname,
|
||||
},
|
||||
'required': ['host'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['remove_host'],
|
||||
'additionalProperties': False,
|
||||
}
|
||||
|
||||
|
||||
set_metadata = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'type': 'object',
|
||||
'set_metadata': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'metadata': parameter_types.metadata_with_null
|
||||
},
|
||||
'required': ['metadata'],
|
||||
'additionalProperties': False,
|
||||
},
|
||||
},
|
||||
'required': ['set_metadata'],
|
||||
'additionalProperties': False,
|
||||
}
|
Loading…
Reference in New Issue
Block a user