blueprint host-aggregates: client bindings
These are the client bindings for the new aggregate api added into nova in the following change: https://review.openstack.org/#change,3109 Change-Id: I97e0223aa18d01450f82848c5be9bce78b83ef39
This commit is contained in:
parent
36be4bf575
commit
0213eb4c0a
1
AUTHORS
1
AUTHORS
@ -24,6 +24,7 @@ Jason Kölker <jason@koelker.net>
|
||||
Jason Straw <jason.straw@rackspace.com>
|
||||
Jesse Andrews <anotherjesse@gmail.com>
|
||||
Johannes Erdfelt <johannes.erdfelt@rackspace.com>
|
||||
John Garbutt <john.garbutt@citrix.com>
|
||||
Josh Kearney <josh@jk0.org>
|
||||
Kevin L. Mitchell <kevin.mitchell@rackspace.com>
|
||||
Kiall Mac Innes <kiall@managedit.ie>
|
||||
|
12
README.rst
12
README.rst
@ -77,6 +77,18 @@ You'll find complete documentation on the shell by running
|
||||
<subcommand>
|
||||
add-fixed-ip Add a new fixed IP address to a servers network.
|
||||
add-floating-ip Add a floating IP address to a server.
|
||||
aggregate-add-host Add the host to the specified aggregate
|
||||
aggregate-create Create a new aggregate with the specified details
|
||||
aggregate-delete Delete the aggregate by its id
|
||||
aggregate-details Show details of the specified aggregate
|
||||
aggregate-list Print a list of all aggregates
|
||||
aggregate-remove-host
|
||||
Remove the specified host from the specfied
|
||||
aggregate
|
||||
aggregate-set-metadata
|
||||
Update the metadata associated with the aggregate
|
||||
aggregate-update Update the aggregate's name and optionally
|
||||
availablity zone
|
||||
backup Backup a server.
|
||||
backup-schedule Show or edit the backup schedule for a server.
|
||||
backup-schedule-delete
|
||||
|
@ -141,6 +141,7 @@ class Manager(utils.HookableMixin):
|
||||
def _update(self, url, body, **kwargs):
|
||||
self.run_hooks('modify_body_for_update', body, **kwargs)
|
||||
resp, body = self.api.client.put(url, body=body)
|
||||
return body
|
||||
|
||||
|
||||
class ManagerWithFind(Manager):
|
||||
|
89
novaclient/v1_1/aggregates.py
Normal file
89
novaclient/v1_1/aggregates.py
Normal file
@ -0,0 +1,89 @@
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
# 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.
|
||||
|
||||
"""Aggregate interface."""
|
||||
|
||||
from novaclient import base
|
||||
|
||||
|
||||
class Aggregate(base.Resource):
|
||||
"""An aggregates is a collection of compute hosts."""
|
||||
|
||||
def __repr__(self):
|
||||
return "<Aggregate: %s>" % self.id
|
||||
|
||||
def update(self, values):
|
||||
"""Update the name and/or availability zone."""
|
||||
return self.manager.update(self, values)
|
||||
|
||||
def add_host(self, host):
|
||||
return self.manager.add_host(self, host)
|
||||
|
||||
def remove_host(self, host):
|
||||
return self.manager.remove_host(self, host)
|
||||
|
||||
def set_metadata(self, metadata):
|
||||
return self.manager.set_metadata(self, metadata)
|
||||
|
||||
def delete(self):
|
||||
self.manager.delete(self)
|
||||
|
||||
|
||||
class AggregateManager(base.ManagerWithFind):
|
||||
resource_class = Aggregate
|
||||
|
||||
def list(self):
|
||||
"""Get a list of os-aggregates."""
|
||||
return self._list('/os-aggregates', 'aggregates')
|
||||
|
||||
def create(self, name, availability_zone):
|
||||
"""Create a new aggregate."""
|
||||
body = {'aggregate': {'name': name,
|
||||
'availability_zone': availability_zone}}
|
||||
return self._create('/os-aggregates', body, 'aggregate')
|
||||
|
||||
def get_details(self, aggregate):
|
||||
"""Get details of the specified aggregate."""
|
||||
return self._get('/os-aggregates/%s' % (base.getid(aggregate)),
|
||||
"aggregate")
|
||||
|
||||
def update(self, aggregate, values):
|
||||
"""Update the name and/or availablity zone."""
|
||||
body = {'aggregate': values}
|
||||
result = self._update("/os-aggregates/%s" % base.getid(aggregate),
|
||||
body)
|
||||
return self.resource_class(self, result["aggregate"])
|
||||
|
||||
def add_host(self, aggregate, host):
|
||||
"""Add a host into the Host Aggregate."""
|
||||
body = {'add_host': {'host': host}}
|
||||
return self._create("/os-aggregates/%s/action" % base.getid(aggregate),
|
||||
body, "aggregate")
|
||||
|
||||
def remove_host(self, aggregate, host):
|
||||
"""Remove a host from the Host Aggregate."""
|
||||
body = {'remove_host': {'host': host}}
|
||||
return self._create("/os-aggregates/%s/action" % base.getid(aggregate),
|
||||
body, "aggregate")
|
||||
|
||||
def set_metadata(self, aggregate, metadata):
|
||||
"""Set a aggregate metadata, replacing the existing metadata."""
|
||||
body = {'set_metadata': {'metadata': metadata}}
|
||||
return self._create("/os-aggregates/%s/action" % base.getid(aggregate),
|
||||
body, "aggregate")
|
||||
|
||||
def delete(self, aggregate):
|
||||
"""Delete the specified aggregates."""
|
||||
self._delete('/os-aggregates/%s' % (base.getid(aggregate)))
|
@ -1,5 +1,6 @@
|
||||
from novaclient import client
|
||||
from novaclient.v1_1 import certs
|
||||
from novaclient.v1_1 import aggregates
|
||||
from novaclient.v1_1 import flavors
|
||||
from novaclient.v1_1 import floating_ip_dns
|
||||
from novaclient.v1_1 import floating_ips
|
||||
@ -64,6 +65,7 @@ class Client(object):
|
||||
self.usage = usage.UsageManager(self)
|
||||
self.virtual_interfaces = \
|
||||
virtual_interfaces.VirtualInterfaceManager(self)
|
||||
self.aggregates = aggregates.AggregateManager(self)
|
||||
|
||||
# Add in any extensions...
|
||||
if extensions:
|
||||
|
@ -380,6 +380,15 @@ def do_image_list(cs, args):
|
||||
def do_image_meta(cs, args):
|
||||
"""Set or Delete metadata on an image."""
|
||||
image = _find_image(cs, args.image)
|
||||
metadata = _extract_metadata(args)
|
||||
|
||||
if args.action == 'set':
|
||||
cs.images.set_meta(image, metadata)
|
||||
elif args.action == 'delete':
|
||||
cs.images.delete_meta(image, metadata.keys())
|
||||
|
||||
|
||||
def _extract_metadata(args):
|
||||
metadata = {}
|
||||
for metadatum in args.metadata[0]:
|
||||
# Can only pass the key in on 'delete'
|
||||
@ -391,11 +400,7 @@ def do_image_meta(cs, args):
|
||||
value = None
|
||||
|
||||
metadata[key] = value
|
||||
|
||||
if args.action == 'set':
|
||||
cs.images.set_meta(image, metadata)
|
||||
elif args.action == 'delete':
|
||||
cs.images.delete_meta(image, metadata.keys())
|
||||
return metadata
|
||||
|
||||
|
||||
def _print_image(image):
|
||||
@ -679,17 +684,7 @@ def do_image_create(cs, args):
|
||||
def do_meta(cs, args):
|
||||
"""Set or Delete metadata on a server."""
|
||||
server = _find_server(cs, args.server)
|
||||
metadata = {}
|
||||
for metadatum in args.metadata[0]:
|
||||
# Can only pass the key in on 'delete'
|
||||
# So this doesn't have to have '='
|
||||
if metadatum.find('=') > -1:
|
||||
(key, value) = metadatum.split('=', 1)
|
||||
else:
|
||||
key = metadatum
|
||||
value = None
|
||||
|
||||
metadata[key] = value
|
||||
metadata = _extract_metadata(args)
|
||||
|
||||
if args.action == 'set':
|
||||
cs.servers.set_meta(server, metadata)
|
||||
@ -1298,7 +1293,7 @@ def do_secgroup_delete_group_rule(cs, args):
|
||||
if (rule.get('ip_protocol') == params.get('ip_protocol') and
|
||||
rule.get('from_port') == params.get('from_port') and
|
||||
rule.get('to_port') == params.get('to_port') and
|
||||
rule.get('group', {}).get('name') ==\
|
||||
rule.get('group', {}).get('name') == \
|
||||
params.get('group_name')):
|
||||
return cs.security_group_rules.delete(rule['id'])
|
||||
|
||||
@ -1445,3 +1440,86 @@ def do_x509_get_root_cert(cs, args):
|
||||
cacert = cs.certs.get()
|
||||
cert.write(cacert.data)
|
||||
print "Wrote x509 root cert to %s" % args.filename
|
||||
|
||||
|
||||
def do_aggregate_list(cs, args):
|
||||
"""Print a list of all aggregates."""
|
||||
aggregates = cs.aggregates.list()
|
||||
columns = ['Id', 'Name', 'Availability Zone', 'Operational State']
|
||||
utils.print_list(aggregates, columns)
|
||||
|
||||
|
||||
@utils.arg('name', metavar='<name>', help='Name of aggregate.')
|
||||
@utils.arg('availability_zone', metavar='<availability_zone>',
|
||||
help='The availablity zone of the aggregate.')
|
||||
def do_aggregate_create(cs, args):
|
||||
"""Create a new aggregate with the specified details."""
|
||||
aggregate = cs.aggregates.create(args.name, args.availability_zone)
|
||||
_print_aggregate_details(aggregate)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Aggregate id to delete.')
|
||||
def do_aggregate_delete(cs, args):
|
||||
"""Delete the aggregate by its id."""
|
||||
cs.aggregates.delete(args.id)
|
||||
print "Aggregate %s has been succesfully deleted." % args.id
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Aggregate id to udpate.')
|
||||
@utils.arg('name', metavar='<name>', help='Name of aggregate.')
|
||||
@utils.arg('availability_zone', metavar='<availability_zone>',
|
||||
help='The availablity zone of the aggregate.', nargs='?')
|
||||
def do_aggregate_update(cs, args):
|
||||
"""Update the aggregate's name and optionally availablity zone."""
|
||||
updates = {"name": args.name}
|
||||
if args.availability_zone:
|
||||
updates["availability_zone"] = args.availability_zone
|
||||
|
||||
aggregate = cs.aggregates.update(args.id, updates)
|
||||
print "Aggregate %s has been succesfully updated." % args.id
|
||||
_print_aggregate_details(aggregate)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Aggregate id to udpate.')
|
||||
@utils.arg('metadata',
|
||||
metavar='<key=value>',
|
||||
nargs='+',
|
||||
action='append',
|
||||
default=[],
|
||||
help='Metadata to add/update to aggregate')
|
||||
def do_aggregate_set_metadata(cs, args):
|
||||
"""Update the metadata associated with the aggregate."""
|
||||
metadata = _extract_metadata(args)
|
||||
aggregate = cs.aggregates.set_metadata(args.id, metadata)
|
||||
print "Aggregate %s has been succesfully updated." % args.id
|
||||
_print_aggregate_details(aggregate)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Host aggregate id to delete.')
|
||||
@utils.arg('host', metavar='<host>', help='The host to add to the aggregate.')
|
||||
def do_aggregate_add_host(cs, args):
|
||||
"""Add the host to the specified aggregate."""
|
||||
aggregate = cs.aggregates.add_host(args.id, args.host)
|
||||
print "Aggregate %s has been succesfully updated." % args.id
|
||||
_print_aggregate_details(aggregate)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Host aggregate id to delete.')
|
||||
@utils.arg('host', metavar='<host>', help='The host to add to the aggregate.')
|
||||
def do_aggregate_remove_host(cs, args):
|
||||
"""Remove the specified host from the specfied aggregate."""
|
||||
aggregate = cs.aggregates.remove_host(args.id, args.host)
|
||||
print "Aggregate %s has been succesfully updated." % args.id
|
||||
_print_aggregate_details(aggregate)
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help='Host aggregate id to delete.')
|
||||
def do_aggregate_details(cs, args):
|
||||
"""Show details of the specified aggregate."""
|
||||
_print_aggregate_details(cs.aggregates.get_details(args.id))
|
||||
|
||||
|
||||
def _print_aggregate_details(aggregate):
|
||||
columns = ['Id', 'Name', 'Availability Zone', 'Operational State',
|
||||
'Hosts', 'Metadata']
|
||||
utils.print_list([aggregate], columns)
|
||||
|
@ -708,3 +708,41 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
|
||||
def post_os_certificates(self, **kw):
|
||||
return (200, {'certificate': {'private_key': 'foo', 'data': 'bar'}})
|
||||
|
||||
#
|
||||
# Aggregates
|
||||
#
|
||||
def get_os_aggregates(self, *kw):
|
||||
return (200, {"aggregates": [
|
||||
{'id':'1',
|
||||
'name': 'test',
|
||||
'availability_zone': 'nova1'},
|
||||
{'id':'2',
|
||||
'name': 'test2',
|
||||
'availability_zone': 'nova1'},
|
||||
]})
|
||||
|
||||
def _return_aggregate(self):
|
||||
r = {'aggregate': self.get_os_aggregates()[1]['aggregates'][0]}
|
||||
return (200, r)
|
||||
|
||||
def get_os_aggregates_1(self, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def post_os_aggregates(self, body, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def put_os_aggregates_1(self, body, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def put_os_aggregates_2(self, body, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def post_os_aggregates_1_action(self, body, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def post_os_aggregates_2_action(self, body, **kw):
|
||||
return self._return_aggregate()
|
||||
|
||||
def delete_os_aggregates_1(self, **kw):
|
||||
return (202, None)
|
||||
|
129
tests/v1_1/test_aggregates.py
Normal file
129
tests/v1_1/test_aggregates.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright 2012 OpenStack LLC.
|
||||
# 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 novaclient.v1_1 import aggregates
|
||||
from tests import utils
|
||||
from tests.v1_1 import fakes
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class AggregatesTest(utils.TestCase):
|
||||
|
||||
def test_list_aggregates(self):
|
||||
result = cs.aggregates.list()
|
||||
cs.assert_called('GET', '/os-aggregates')
|
||||
for aggregate in result:
|
||||
self.assertTrue(isinstance(aggregate, aggregates.Aggregate))
|
||||
|
||||
def test_create_aggregate(self):
|
||||
body = {"aggregate": {"name": "test", "availability_zone": "nova1"}}
|
||||
aggregate = cs.aggregates.create("test", "nova1")
|
||||
cs.assert_called('POST', '/os-aggregates', body)
|
||||
self.assertTrue(isinstance(aggregate, aggregates.Aggregate))
|
||||
|
||||
def test_get_details(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
cs.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertTrue(isinstance(aggregate, aggregates.Aggregate))
|
||||
|
||||
aggregate2 = cs.aggregates.get_details(aggregate)
|
||||
cs.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertTrue(isinstance(aggregate2, aggregates.Aggregate))
|
||||
|
||||
def test_update(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
values = {"name": "foo"}
|
||||
body = {"aggregate": values}
|
||||
|
||||
result1 = aggregate.update(values)
|
||||
cs.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertTrue(isinstance(result1, aggregates.Aggregate))
|
||||
|
||||
result2 = cs.aggregates.update(2, values)
|
||||
cs.assert_called('PUT', '/os-aggregates/2', body)
|
||||
self.assertTrue(isinstance(result2, aggregates.Aggregate))
|
||||
|
||||
def test_update_with_availablity_zone(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
values = {"name": "foo", "availability_zone": "new_zone"}
|
||||
body = {"aggregate": values}
|
||||
|
||||
result3 = cs.aggregates.update(aggregate, values)
|
||||
cs.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertTrue(isinstance(result3, aggregates.Aggregate))
|
||||
|
||||
def test_add_host(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
host = "host1"
|
||||
body = {"add_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.add_host(host)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result1, aggregates.Aggregate))
|
||||
|
||||
result2 = cs.aggregates.add_host("2", host)
|
||||
cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertTrue(isinstance(result2, aggregates.Aggregate))
|
||||
|
||||
result3 = cs.aggregates.add_host(aggregate, host)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result3, aggregates.Aggregate))
|
||||
|
||||
def test_remove_host(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
host = "host1"
|
||||
body = {"remove_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.remove_host(host)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result1, aggregates.Aggregate))
|
||||
|
||||
result2 = cs.aggregates.remove_host("2", host)
|
||||
cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertTrue(isinstance(result2, aggregates.Aggregate))
|
||||
|
||||
result3 = cs.aggregates.remove_host(aggregate, host)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result3, aggregates.Aggregate))
|
||||
|
||||
def test_set_metadata(self):
|
||||
aggregate = cs.aggregates.get_details("1")
|
||||
metadata = {"foo": "bar"}
|
||||
body = {"set_metadata": {"metadata": metadata}}
|
||||
|
||||
result1 = aggregate.set_metadata(metadata)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result1, aggregates.Aggregate))
|
||||
|
||||
result2 = cs.aggregates.set_metadata(2, metadata)
|
||||
cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertTrue(isinstance(result2, aggregates.Aggregate))
|
||||
|
||||
result3 = cs.aggregates.set_metadata(aggregate, metadata)
|
||||
cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertTrue(isinstance(result3, aggregates.Aggregate))
|
||||
|
||||
def test_delete_aggregate(self):
|
||||
aggregate = cs.aggregates.list()[0]
|
||||
aggregate.delete()
|
||||
cs.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
cs.aggregates.delete('1')
|
||||
cs.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
cs.aggregates.delete(aggregate)
|
||||
cs.assert_called('DELETE', '/os-aggregates/1')
|
@ -387,3 +387,47 @@ class ShellTest(utils.TestCase):
|
||||
|
||||
self.assert_called('POST', '/flavors', body, pos=-2)
|
||||
self.assert_called('GET', '/flavors/1')
|
||||
|
||||
def test_aggregate_list(self):
|
||||
self.run_command('aggregate-list')
|
||||
self.assert_called('GET', '/os-aggregates')
|
||||
|
||||
def test_aggregate_create(self):
|
||||
self.run_command('aggregate-create test_name nova1')
|
||||
body = {"aggregate": {"name": "test_name",
|
||||
"availability_zone": "nova1"}}
|
||||
self.assert_called('POST', '/os-aggregates', body)
|
||||
|
||||
def test_aggregate_delete(self):
|
||||
self.run_command('aggregate-delete 1')
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
def test_aggregate_update(self):
|
||||
self.run_command('aggregate-update 1 new_name')
|
||||
body = {"aggregate": {"name": "new_name"}}
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
|
||||
def test_aggregate_update_with_availablity_zone(self):
|
||||
self.run_command('aggregate-update 1 foo new_zone')
|
||||
body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}}
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
|
||||
def test_aggregate_set_metadata(self):
|
||||
self.run_command('aggregate-set-metadata 1 foo=bar delete_key')
|
||||
body = {"set_metadata": {"metadata": {"foo": "bar",
|
||||
"delete_key": None}}}
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
|
||||
def test_aggregate_add_host(self):
|
||||
self.run_command('aggregate-add-host 1 host1')
|
||||
body = {"add_host": {"host": "host1"}}
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
|
||||
def test_aggregate_remove_host(self):
|
||||
self.run_command('aggregate-remove-host 1 host1')
|
||||
body = {"remove_host": {"host": "host1"}}
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
|
||||
def test_aggregate_details(self):
|
||||
self.run_command('aggregate-details 1')
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
|
Loading…
x
Reference in New Issue
Block a user