Remove the Nova aggregate files.

There was a commit [0] that dealt with Nova aggs that also updated
doc/source/user/placement.rst. Since that file was preserved in the
filter_history.sh script, that commit caused a lot of agg-related files
to be preserved. This commit removes most of them; others in the test
directories will be removed in a later patch.

[0] https://review.openstack.org/#/c/553597/

Change-Id: I2b0ee94c1d7f11df542ec5171278696fb21b11d1
This commit is contained in:
EdLeafe 2018-08-30 22:46:35 +00:00 committed by Eric Fried
parent f849f668a9
commit ae561723ee
52 changed files with 0 additions and 1572 deletions

View File

@ -1,5 +0,0 @@
{
"add_host": {
"host": "21549b2f665945baaa7101926a00143c"
}
}

View File

@ -1,9 +0,0 @@
{
"set_metadata":
{
"metadata":
{
"key": "value"
}
}
}

View File

@ -1,7 +0,0 @@
{
"aggregate":
{
"name": "name",
"availability_zone": "london"
}
}

View File

@ -1,11 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2013-08-18T12:17:55.751757",
"deleted": false,
"deleted_at": null,
"id": 1,
"name": "name",
"updated_at": null
}
}

View File

@ -1,5 +0,0 @@
{
"remove_host": {
"host": "bf1454b3d71145d49fca2101c56c728d"
}
}

View File

@ -1,7 +0,0 @@
{
"aggregate":
{
"name": "newname",
"availability_zone": "nova2"
}
}

View File

@ -1,15 +0,0 @@
{
"aggregate": {
"availability_zone": "nova2",
"created_at": "2013-08-18T12:17:56.259751",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "nova2"
},
"name": "newname",
"updated_at": "2013-08-18T12:17:56.286720"
}
}

View File

@ -1,17 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2013-08-18T12:17:56.297823",
"deleted": false,
"deleted_at": null,
"hosts": [
"21549b2f665945baaa7101926a00143c"
],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null
}
}

View File

@ -1,15 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2013-08-18T12:17:56.380226",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null
}
}

View File

@ -1,17 +0,0 @@
{
"aggregates": [
{
"availability_zone": "london",
"created_at": "2013-08-18T12:17:56.856455",
"deleted": false,
"deleted_at": null,
"hosts": ["21549b2f665945baaa7101926a00143c"],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null
}
]
}

View File

@ -1,16 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2013-08-18T12:17:55.959571",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london",
"key": "value"
},
"name": "name",
"updated_at": "2013-08-18T12:17:55.986540"
}
}

View File

@ -1,15 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2013-08-18T12:17:56.990581",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null
}
}

View File

@ -1,5 +0,0 @@
{
"add_host": {
"host": "compute"
}
}

View File

@ -1,9 +0,0 @@
{
"set_metadata":
{
"metadata":
{
"key": "value"
}
}
}

View File

@ -1,7 +0,0 @@
{
"aggregate":
{
"name": "name",
"availability_zone": "nova"
}
}

View File

@ -1,12 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T22:51:32.877711",
"deleted": false,
"deleted_at": null,
"id": 1,
"name": "name",
"updated_at": null,
"uuid": "86a0da0e-9f0c-4f51-a1e0-3c25edab3783"
}
}

View File

@ -1,5 +0,0 @@
{
"remove_host": {
"host": "compute"
}
}

View File

@ -1,7 +0,0 @@
{
"aggregate":
{
"name": "newname",
"availability_zone": "nova2"
}
}

View File

@ -1,16 +0,0 @@
{
"aggregate": {
"availability_zone": "nova2",
"created_at": "2016-12-27T23:47:32.897139",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "nova2"
},
"name": "newname",
"updated_at": "2016-12-27T23:47:33.067180",
"uuid": "6f74e3f3-df28-48f3-98e1-ac941b1c5e43"
}
}

View File

@ -1,18 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T23:47:30.594805",
"deleted": false,
"deleted_at": null,
"hosts": [
"compute"
],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null,
"uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456"
}
}

View File

@ -1,16 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T23:47:30.563527",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null,
"uuid": "fd0a5b12-7e8d-469d-bfd5-64a6823e7407"
}
}

View File

@ -1,20 +0,0 @@
{
"aggregates": [
{
"availability_zone": "london",
"created_at": "2016-12-27T23:47:32.911515",
"deleted": false,
"deleted_at": null,
"hosts": [
"compute"
],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null,
"uuid": "6ba28ba7-f29b-45cc-a30b-6e3a40c2fb14"
}
]
}

View File

@ -1,17 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T23:59:18.623100",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london",
"key": "value"
},
"name": "name",
"updated_at": "2016-12-27T23:59:18.723348",
"uuid": "26002bdb-62cc-41bd-813a-0ad22db32625"
}
}

View File

@ -1,16 +0,0 @@
{
"aggregate": {
"availability_zone": "london",
"created_at": "2016-12-27T23:47:30.594805",
"deleted": false,
"deleted_at": null,
"hosts": [],
"id": 1,
"metadata": {
"availability_zone": "london"
},
"name": "name",
"updated_at": null,
"uuid": "d1842372-89c5-4fbd-ad5a-5d2e16c85456"
}
}

View File

@ -1,11 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"hosts": ["compute"]
}
},
"event_type": "aggregate.add_host.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.add_host.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.create.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,12 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"hosts": null,
"id": null
}
},
"event_type": "aggregate.create.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.delete.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.delete.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.remove_host.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,11 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"hosts": ["compute"]
}
},
"event_type": "aggregate.remove_host.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,13 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"metadata": {
"availability_zone": "AZ-1"
}
}
},
"event_type": "aggregate.update_metadata.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,8 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#"
},
"event_type": "aggregate.update_metadata.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,11 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"name": "my-new-aggregate"
}
},
"event_type": "aggregate.update_prop.end",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,11 +0,0 @@
{
"priority": "INFO",
"payload": {
"$ref": "common_payloads/AggregatePayload.json#",
"nova_object.data": {
"name": "my-new-aggregate"
}
},
"event_type": "aggregate.update_prop.start",
"publisher_id": "nova-api:fake-mini"
}

View File

@ -1,14 +0,0 @@
{
"nova_object.version": "1.1",
"nova_object.namespace": "nova",
"nova_object.name": "AggregatePayload",
"nova_object.data": {
"name": "my-aggregate",
"metadata": {
"availability_zone": "nova"
},
"uuid": "788608ec-ebdc-45c5-bc7f-e5f24ab92c80",
"hosts": [],
"id": 1
}
}

View File

@ -1,139 +0,0 @@
..
Copyright 2012 OpenStack Foundation
Copyright 2012 Citrix Systems, Inc.
Copyright 2012, The Cloudscaling Group, 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.
Host Aggregates
===============
Host aggregates can be regarded as a mechanism to further partition an
availability zone; while availability zones are visible to users, host
aggregates are only visible to administrators. Host aggregates started out as
a way to use Xen hypervisor resource pools, but have been generalized to provide
a mechanism to allow administrators to assign key-value pairs to groups of
machines. Each node can have multiple aggregates, each aggregate can have
multiple key-value pairs, and the same key-value pair can be assigned to
multiple aggregates. This information can be used in the scheduler to enable
advanced scheduling, to set up Xen hypervisor resource pools or to define
logical groups for migration. For more information, including an example of
associating a group of hosts to a flavor, see :ref:`host-aggregates`.
Availability Zones (AZs)
------------------------
Availability Zones are the end-user visible logical abstraction for
partitioning a cloud without knowing the physical infrastructure.
That abstraction doesn't come up in Nova with an actual database model since
the availability zone is actually a specific metadata information attached to
an aggregate. Adding that specific metadata to an aggregate makes the aggregate
visible from an end-user perspective and consequently allows to schedule upon a
specific set of hosts (the ones belonging to the aggregate).
That said, there are a few rules to know that diverge from an API perspective
between aggregates and availability zones:
- one host can be in multiple aggregates, but it can only be in one
availability zone
- by default a host is part of a default availability zone even if it doesn't
belong to an aggregate (the configuration option is named
``default_availability_zone``)
.. warning:: That last rule can be very error-prone. Since the user can see the
list of availability zones, they have no way to know whether the default
availability zone name (currently *nova*) is provided because an host
belongs to an aggregate whose AZ metadata key is set to *nova*, or because
there is at least one host not belonging to any aggregate. Consequently, it is
highly recommended for users to never ever ask for booting an instance by
specifying an explicit AZ named *nova* and for operators to never set the
AZ metadata for an aggregate to *nova*. That leads to some problems
due to the fact that the instance AZ information is explicitly attached to
*nova* which could break further move operations when either the host is
moved to another aggregate or when the user would like to migrate the
instance.
.. note:: Availability zone name must NOT contain ':' since it is used by admin
users to specify hosts where instances are launched in server creation.
See :doc:`Select hosts where instances are launched </admin/availability-zones>` for more detail.
There is a nice educational video about availability zones from the Rocky
summit which can be found here: https://www.openstack.org/videos/vancouver-2018/curse-your-bones-availability-zones-1
Design
------
The OSAPI Admin API is extended to support the following operations:
* Aggregates
* list aggregates: returns a list of all the host-aggregates
* create aggregate: creates an aggregate, takes a friendly name, etc. returns an id
* show aggregate: shows the details of an aggregate (id, name, availability_zone, hosts and metadata)
* update aggregate: updates the name and availability zone of an aggregate
* set metadata: sets the metadata on an aggregate to the values supplied
* delete aggregate: deletes an aggregate, it fails if the aggregate is not empty
* add host: adds a host to the aggregate
* remove host: removes a host from the aggregate
* Hosts
* list all hosts by service
* It has been deprecated since microversion 2.43. Use `list hypervisors` instead.
* start host maintenance (or evacuate-host): disallow a host to serve API requests and migrate instances to other hosts of the aggregate
* It has been deprecated since microversion 2.43. Use `disable service` instead.
* stop host maintenance (or rebalance-host): put the host back into operational mode, migrating instances back onto that host
* It has been deprecated since microversion 2.43. Use `enable service` instead.
* Hypervisors
* list hypervisors: list hypervisors with hypervisor hostname
* Compute services
* enable service
* disable service
Using the Nova CLI
------------------
Using the nova command you can create, delete and manage aggregates. The following section outlines the list of available commands.
Usage
~~~~~
::
* aggregate-list Print a list of all aggregates.
* aggregate-create <name> [<availability_zone>] Create a new aggregate with the specified details.
* aggregate-delete <aggregate> Delete the aggregate by its ID or name.
* aggregate-show <aggregate> Show details of the aggregate specified by its ID or name.
* aggregate-add-host <aggregate> <host> Add the host to the aggregate specified by its ID or name.
* aggregate-remove-host <aggregate> <host> Remove the specified host from the aggregate specified by its ID or name.
* aggregate-set-metadata <aggregate> <key=value> [<key=value> ...]
Update the metadata associated with the aggregate specified by its ID or name.
* aggregate-update [--name <name>] [--availability-zone <availability-zone>] <aggregate>
Update the aggregate's name or availability zone.
* host-list List all hosts by service.
* hypervisor-list [--matching <hostname>] [--marker <marker>] [--limit <limit>]
List hypervisors.
* host-update [--status <enable|disable>] [--maintenance <enable|disable>] <hostname>
Put/resume host into/from maintenance.
* service-enable <id> Enable the service.
* service-disable [--reason <reason>] <id> Disable the service.

View File

@ -1,64 +0,0 @@
# 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.
from nova.notifications.objects import base
from nova.objects import base as nova_base
from nova.objects import fields
@nova_base.NovaObjectRegistry.register_notification
class AggregatePayload(base.NotificationPayloadBase):
SCHEMA = {
'id': ('aggregate', 'id'),
'uuid': ('aggregate', 'uuid'),
'name': ('aggregate', 'name'),
'hosts': ('aggregate', 'hosts'),
'metadata': ('aggregate', 'metadata'),
}
# Version 1.0: Initial version
# 1.1: Making the id field nullable
VERSION = '1.1'
fields = {
# NOTE(gibi): id is nullable as aggregate.create.start is sent before
# the id is generated by the db
'id': fields.IntegerField(nullable=True),
'uuid': fields.UUIDField(nullable=False),
'name': fields.StringField(),
'hosts': fields.ListOfStringsField(nullable=True),
'metadata': fields.DictOfStringsField(nullable=True),
}
def __init__(self, aggregate):
super(AggregatePayload, self).__init__()
self.populate_schema(aggregate=aggregate)
@base.notification_sample('aggregate-create-start.json')
@base.notification_sample('aggregate-create-end.json')
@base.notification_sample('aggregate-delete-start.json')
@base.notification_sample('aggregate-delete-end.json')
@base.notification_sample('aggregate-add_host-start.json')
@base.notification_sample('aggregate-add_host-end.json')
@base.notification_sample('aggregate-remove_host-start.json')
@base.notification_sample('aggregate-remove_host-end.json')
@base.notification_sample('aggregate-update_metadata-start.json')
@base.notification_sample('aggregate-update_metadata-end.json')
@base.notification_sample('aggregate-update_prop-start.json')
@base.notification_sample('aggregate-update_prop-end.json')
@nova_base.NovaObjectRegistry.register_notification
class AggregateNotification(base.NotificationBase):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'payload': fields.ObjectField('AggregatePayload')
}

View File

@ -1,509 +0,0 @@
# Copyright 2013 IBM Corp.
#
# 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.
from oslo_db import exception as db_exc
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import uuidutils
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm import joinedload
from nova.compute import utils as compute_utils
from nova.db.sqlalchemy import api as db_api
from nova.db.sqlalchemy import api_models
from nova import exception
from nova.i18n import _
from nova import objects
from nova.objects import base
from nova.objects import fields
LOG = logging.getLogger(__name__)
DEPRECATED_FIELDS = ['deleted', 'deleted_at']
@db_api.api_context_manager.reader
def _aggregate_get_from_db(context, aggregate_id):
query = context.session.query(api_models.Aggregate).\
options(joinedload('_hosts')).\
options(joinedload('_metadata'))
query = query.filter(api_models.Aggregate.id == aggregate_id)
aggregate = query.first()
if not aggregate:
raise exception.AggregateNotFound(aggregate_id=aggregate_id)
return aggregate
@db_api.api_context_manager.reader
def _aggregate_get_from_db_by_uuid(context, aggregate_uuid):
query = context.session.query(api_models.Aggregate).\
options(joinedload('_hosts')).\
options(joinedload('_metadata'))
query = query.filter(api_models.Aggregate.uuid == aggregate_uuid)
aggregate = query.first()
if not aggregate:
raise exception.AggregateNotFound(aggregate_id=aggregate_uuid)
return aggregate
def _host_add_to_db(context, aggregate_id, host):
try:
with db_api.api_context_manager.writer.using(context):
# Check to see if the aggregate exists
_aggregate_get_from_db(context, aggregate_id)
host_ref = api_models.AggregateHost()
host_ref.update({"host": host, "aggregate_id": aggregate_id})
host_ref.save(context.session)
return host_ref
except db_exc.DBDuplicateEntry:
raise exception.AggregateHostExists(host=host,
aggregate_id=aggregate_id)
def _host_delete_from_db(context, aggregate_id, host):
count = 0
with db_api.api_context_manager.writer.using(context):
# Check to see if the aggregate exists
_aggregate_get_from_db(context, aggregate_id)
query = context.session.query(api_models.AggregateHost)
query = query.filter(api_models.AggregateHost.aggregate_id ==
aggregate_id)
count = query.filter_by(host=host).delete()
if count == 0:
raise exception.AggregateHostNotFound(aggregate_id=aggregate_id,
host=host)
def _metadata_add_to_db(context, aggregate_id, metadata, max_retries=10,
set_delete=False):
all_keys = metadata.keys()
for attempt in range(max_retries):
try:
with db_api.api_context_manager.writer.using(context):
query = context.session.query(api_models.AggregateMetadata).\
filter_by(aggregate_id=aggregate_id)
if set_delete:
query.filter(~api_models.AggregateMetadata.key.
in_(all_keys)).\
delete(synchronize_session=False)
already_existing_keys = set()
if all_keys:
query = query.filter(
api_models.AggregateMetadata.key.in_(all_keys))
for meta_ref in query.all():
key = meta_ref.key
meta_ref.update({"value": metadata[key]})
already_existing_keys.add(key)
new_entries = []
for key, value in metadata.items():
if key in already_existing_keys:
continue
new_entries.append({"key": key,
"value": value,
"aggregate_id": aggregate_id})
if new_entries:
context.session.execute(
api_models.AggregateMetadata.__table__.insert(),
new_entries)
return metadata
except db_exc.DBDuplicateEntry:
# a concurrent transaction has been committed,
# try again unless this was the last attempt
with excutils.save_and_reraise_exception() as ctxt:
if attempt < max_retries - 1:
ctxt.reraise = False
else:
msg = _("Add metadata failed for aggregate %(id)s "
"after %(retries)s retries") % \
{"id": aggregate_id, "retries": max_retries}
LOG.warning(msg)
@db_api.api_context_manager.writer
def _metadata_delete_from_db(context, aggregate_id, key):
# Check to see if the aggregate exists
_aggregate_get_from_db(context, aggregate_id)
query = context.session.query(api_models.AggregateMetadata)
query = query.filter(api_models.AggregateMetadata.aggregate_id ==
aggregate_id)
count = query.filter_by(key=key).delete()
if count == 0:
raise exception.AggregateMetadataNotFound(
aggregate_id=aggregate_id, metadata_key=key)
@db_api.api_context_manager.writer
def _aggregate_create_in_db(context, values, metadata=None):
query = context.session.query(api_models.Aggregate)
query = query.filter(api_models.Aggregate.name == values['name'])
aggregate = query.first()
if not aggregate:
aggregate = api_models.Aggregate()
aggregate.update(values)
aggregate.save(context.session)
# We don't want these to be lazy loaded later. We know there is
# nothing here since we just created this aggregate.
aggregate._hosts = []
aggregate._metadata = []
else:
raise exception.AggregateNameExists(aggregate_name=values['name'])
if metadata:
_metadata_add_to_db(context, aggregate.id, metadata)
context.session.expire(aggregate, ['_metadata'])
aggregate._metadata
return aggregate
@db_api.api_context_manager.writer
def _aggregate_delete_from_db(context, aggregate_id):
# Delete Metadata first
context.session.query(api_models.AggregateMetadata).\
filter_by(aggregate_id=aggregate_id).\
delete()
count = context.session.query(api_models.Aggregate).\
filter(api_models.Aggregate.id == aggregate_id).\
delete()
if count == 0:
raise exception.AggregateNotFound(aggregate_id=aggregate_id)
@db_api.api_context_manager.writer
def _aggregate_update_to_db(context, aggregate_id, values):
aggregate = _aggregate_get_from_db(context, aggregate_id)
set_delete = True
if "availability_zone" in values:
az = values.pop('availability_zone')
if 'metadata' not in values:
values['metadata'] = {'availability_zone': az}
set_delete = False
else:
values['metadata']['availability_zone'] = az
metadata = values.get('metadata')
if metadata is not None:
_metadata_add_to_db(context, aggregate_id, values.pop('metadata'),
set_delete=set_delete)
aggregate.update(values)
try:
aggregate.save(context.session)
except db_exc.DBDuplicateEntry:
if 'name' in values:
raise exception.AggregateNameExists(
aggregate_name=values['name'])
else:
raise
return _aggregate_get_from_db(context, aggregate_id)
@base.NovaObjectRegistry.register
class Aggregate(base.NovaPersistentObject, base.NovaObject):
# Version 1.0: Initial version
# Version 1.1: String attributes updated to support unicode
# Version 1.2: Added uuid field
# Version 1.3: Added get_by_uuid method
VERSION = '1.3'
fields = {
'id': fields.IntegerField(),
'uuid': fields.UUIDField(nullable=False),
'name': fields.StringField(),
'hosts': fields.ListOfStringsField(nullable=True),
'metadata': fields.DictOfStringsField(nullable=True),
}
obj_extra_fields = ['availability_zone']
@staticmethod
def _from_db_object(context, aggregate, db_aggregate):
for key in aggregate.fields:
if key == 'metadata':
db_key = 'metadetails'
elif key in DEPRECATED_FIELDS and key not in db_aggregate:
continue
else:
db_key = key
setattr(aggregate, key, db_aggregate[db_key])
# NOTE: This can be removed when we bump Aggregate to v2.0
aggregate.deleted_at = None
aggregate.deleted = False
aggregate._context = context
aggregate.obj_reset_changes()
return aggregate
def _assert_no_hosts(self, action):
if 'hosts' in self.obj_what_changed():
raise exception.ObjectActionError(
action=action,
reason='hosts updated inline')
@base.remotable_classmethod
def get_by_id(cls, context, aggregate_id):
db_aggregate = _aggregate_get_from_db(context, aggregate_id)
return cls._from_db_object(context, cls(), db_aggregate)
@base.remotable_classmethod
def get_by_uuid(cls, context, aggregate_uuid):
db_aggregate = _aggregate_get_from_db_by_uuid(context,
aggregate_uuid)
return cls._from_db_object(context, cls(), db_aggregate)
@base.remotable
def create(self):
if self.obj_attr_is_set('id'):
raise exception.ObjectActionError(action='create',
reason='already created')
self._assert_no_hosts('create')
updates = self.obj_get_changes()
payload = dict(updates)
if 'metadata' in updates:
# NOTE(danms): For some reason the notification format is weird
payload['meta_data'] = payload.pop('metadata')
if 'uuid' not in updates:
updates['uuid'] = uuidutils.generate_uuid()
self.uuid = updates['uuid']
LOG.debug('Generated uuid %(uuid)s for aggregate',
dict(uuid=updates['uuid']))
compute_utils.notify_about_aggregate_update(self._context,
"create.start",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.START)
metadata = updates.pop('metadata', None)
db_aggregate = _aggregate_create_in_db(self._context, updates,
metadata=metadata)
self._from_db_object(self._context, self, db_aggregate)
payload['aggregate_id'] = self.id
compute_utils.notify_about_aggregate_update(self._context,
"create.end",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.CREATE,
phase=fields.NotificationPhase.END)
@base.remotable
def save(self):
self._assert_no_hosts('save')
updates = self.obj_get_changes()
payload = {'aggregate_id': self.id}
if 'metadata' in updates:
payload['meta_data'] = updates['metadata']
compute_utils.notify_about_aggregate_update(self._context,
"updateprop.start",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.UPDATE_PROP,
phase=fields.NotificationPhase.START)
updates.pop('id', None)
db_aggregate = _aggregate_update_to_db(self._context,
self.id, updates)
compute_utils.notify_about_aggregate_update(self._context,
"updateprop.end",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.UPDATE_PROP,
phase=fields.NotificationPhase.END)
self._from_db_object(self._context, self, db_aggregate)
@base.remotable
def update_metadata(self, updates):
payload = {'aggregate_id': self.id,
'meta_data': updates}
compute_utils.notify_about_aggregate_update(self._context,
"updatemetadata.start",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.UPDATE_METADATA,
phase=fields.NotificationPhase.START)
to_add = {}
for key, value in updates.items():
if value is None:
try:
_metadata_delete_from_db(self._context, self.id, key)
except exception.AggregateMetadataNotFound:
pass
try:
self.metadata.pop(key)
except KeyError:
pass
else:
to_add[key] = value
self.metadata[key] = value
_metadata_add_to_db(self._context, self.id, to_add)
compute_utils.notify_about_aggregate_update(self._context,
"updatemetadata.end",
payload)
compute_utils.notify_about_aggregate_action(
context=self._context,
aggregate=self,
action=fields.NotificationAction.UPDATE_METADATA,
phase=fields.NotificationPhase.END)
self.obj_reset_changes(fields=['metadata'])
@base.remotable
def destroy(self):
_aggregate_delete_from_db(self._context, self.id)
@base.remotable
def add_host(self, host):
_host_add_to_db(self._context, self.id, host)
if self.hosts is None:
self.hosts = []
self.hosts.append(host)
self.obj_reset_changes(fields=['hosts'])
@base.remotable
def delete_host(self, host):
_host_delete_from_db(self._context, self.id, host)
self.hosts.remove(host)
self.obj_reset_changes(fields=['hosts'])
@property
def availability_zone(self):
return self.metadata.get('availability_zone', None)
@db_api.api_context_manager.reader
def _get_all_from_db(context):
query = context.session.query(api_models.Aggregate).\
options(joinedload('_hosts')).\
options(joinedload('_metadata'))
return query.all()
@db_api.api_context_manager.reader
def _get_by_host_from_db(context, host, key=None):
query = context.session.query(api_models.Aggregate).\
options(joinedload('_hosts')).\
options(joinedload('_metadata'))
query = query.join('_hosts')
query = query.filter(api_models.AggregateHost.host == host)
if key:
query = query.join("_metadata").filter(
api_models.AggregateMetadata.key == key)
return query.all()
@db_api.api_context_manager.reader
def _get_by_metadata_from_db(context, key=None, value=None):
assert(key is not None or value is not None)
query = context.session.query(api_models.Aggregate)
query = query.join("_metadata")
if key is not None:
query = query.filter(api_models.AggregateMetadata.key == key)
if value is not None:
query = query.filter(api_models.AggregateMetadata.value == value)
query = query.options(contains_eager("_metadata"))
query = query.options(joinedload("_hosts"))
return query.all()
@base.NovaObjectRegistry.register
class AggregateList(base.ObjectListBase, base.NovaObject):
# Version 1.0: Initial version
# Version 1.1: Added key argument to get_by_host()
# Aggregate <= version 1.1
# Version 1.2: Added get_by_metadata_key
# Version 1.3: Added get_by_metadata
VERSION = '1.3'
fields = {
'objects': fields.ListOfObjectsField('Aggregate'),
}
@classmethod
def _filter_db_aggregates(cls, db_aggregates, hosts):
if not isinstance(hosts, set):
hosts = set(hosts)
filtered_aggregates = []
for db_aggregate in db_aggregates:
for host in db_aggregate['hosts']:
if host in hosts:
filtered_aggregates.append(db_aggregate)
break
return filtered_aggregates
@base.remotable_classmethod
def get_all(cls, context):
db_aggregates = _get_all_from_db(context)
return base.obj_make_list(context, cls(context), objects.Aggregate,
db_aggregates)
@base.remotable_classmethod
def get_by_host(cls, context, host, key=None):
db_aggregates = _get_by_host_from_db(context, host, key=key)
return base.obj_make_list(context, cls(context), objects.Aggregate,
db_aggregates)
@base.remotable_classmethod
def get_by_metadata_key(cls, context, key, hosts=None):
db_aggregates = _get_by_metadata_from_db(context, key=key)
if hosts is not None:
db_aggregates = cls._filter_db_aggregates(db_aggregates, hosts)
return base.obj_make_list(context, cls(context), objects.Aggregate,
db_aggregates)
@base.remotable_classmethod
def get_by_metadata(cls, context, key=None, value=None):
"""Return aggregates with a metadata key set to value.
This returns a list of all aggregates that have a metadata key
set to some value. If key is specified, then only values for
that key will qualify.
"""
db_aggregates = _get_by_metadata_from_db(context, key=key, value=value)
return base.obj_make_list(context, cls(context), objects.Aggregate,
db_aggregates)

View File

@ -1,109 +0,0 @@
# Copyright 2016 Cloudbase Solutions Srl
# 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.
from oslo_policy import policy
from nova.policies import base
POLICY_ROOT = 'os_compute_api:os-aggregates:%s'
aggregates_policies = [
policy.DocumentedRuleDefault(
POLICY_ROOT % 'set_metadata',
base.RULE_ADMIN_API,
"Create or replace metadata for an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}/action (set_metadata)',
'method': 'POST'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'add_host',
base.RULE_ADMIN_API,
"Add a host to an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}/action (add_host)',
'method': 'POST'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'create',
base.RULE_ADMIN_API,
"Create an aggregate",
[
{
'path': '/os-aggregates',
'method': 'POST'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'remove_host',
base.RULE_ADMIN_API,
"Remove a host from an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}/action (remove_host)',
'method': 'POST'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'update',
base.RULE_ADMIN_API,
"Update name and/or availability zone for an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}',
'method': 'PUT'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'index',
base.RULE_ADMIN_API,
"List all aggregates",
[
{
'path': '/os-aggregates',
'method': 'GET'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'delete',
base.RULE_ADMIN_API,
"Delete an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}',
'method': 'DELETE'
}
]),
policy.DocumentedRuleDefault(
POLICY_ROOT % 'show',
base.RULE_ADMIN_API,
"Show details for an aggregate",
[
{
'path': '/os-aggregates/{aggregate_id}',
'method': 'GET'
}
]),
]
def list_rules():
return aggregates_policies

View File

@ -1,72 +0,0 @@
# Copyright (c) 2013 Cloudwatt
# 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.
from oslo_log import log as logging
import nova.conf
from nova.scheduler import filters
from nova.scheduler.filters import utils
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
class AggregateImagePropertiesIsolation(filters.BaseHostFilter):
"""AggregateImagePropertiesIsolation works with image properties."""
# Aggregate data and instance type does not change within a request
run_filter_once_per_request = True
RUN_ON_REBUILD = True
def host_passes(self, host_state, spec_obj):
"""Checks a host in an aggregate that metadata key/value match
with image properties.
"""
cfg_namespace = (CONF.filter_scheduler.
aggregate_image_properties_isolation_namespace)
cfg_separator = (CONF.filter_scheduler.
aggregate_image_properties_isolation_separator)
image_props = spec_obj.image.properties if spec_obj.image else {}
metadata = utils.aggregate_metadata_get_by_host(host_state)
for key, options in metadata.items():
if (cfg_namespace and
not key.startswith(cfg_namespace + cfg_separator)):
continue
prop = None
try:
prop =