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:
parent
f849f668a9
commit
ae561723ee
@ -1,5 +0,0 @@
|
||||
{
|
||||
"add_host": {
|
||||
"host": "21549b2f665945baaa7101926a00143c"
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"set_metadata":
|
||||
{
|
||||
"metadata":
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"aggregate":
|
||||
{
|
||||
"name": "name",
|
||||
"availability_zone": "london"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"remove_host": {
|
||||
"host": "bf1454b3d71145d49fca2101c56c728d"
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"aggregate":
|
||||
{
|
||||
"name": "newname",
|
||||
"availability_zone": "nova2"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"add_host": {
|
||||
"host": "compute"
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"set_metadata":
|
||||
{
|
||||
"metadata":
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"aggregate":
|
||||
{
|
||||
"name": "name",
|
||||
"availability_zone": "nova"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"remove_host": {
|
||||
"host": "compute"
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"aggregate":
|
||||
{
|
||||
"name": "newname",
|
||||
"availability_zone": "nova2"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.add_host.start",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.create.end",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -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"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.delete.end",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.delete.start",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.remove_host.end",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"priority": "INFO",
|
||||
"payload": {
|
||||
"$ref": "common_payloads/AggregatePayload.json#"
|
||||
},
|
||||
"event_type": "aggregate.update_metadata.start",
|
||||
"publisher_id": "nova-api:fake-mini"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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.
|
@ -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')
|
||||
}
|
@ -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)
|
@ -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
|
@ -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 = |