From 3403e773f5a38c5d415e4ab66799c6e239223a0d Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 19:40:35 -0500 Subject: Stubbed-out code for working with provider-firewalls. --- nova/api/ec2/admin.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 758b612e8..b784c5a00 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -209,3 +209,11 @@ class AdminController(object): def describe_host(self, _context, name, **_kwargs): """Returns status info for single node.""" return host_dict(db.host_get(name)) + + def block_external_addresses(self, context, cidr): + """Add provider-level firewall rules to block incoming traffic.""" + LOG.audit(_("Blocking access to all projects incoming from %s"), + cidr, context=context) + raise NotImplementedError(_("Awaiting implementation.")) + # TODO(todd): implement + # return {'status': 'OK', 'message': 'Disabled (number) IPs'} -- cgit From d4e7eb818c9f4ec51fd3a88a0e92d557867511d4 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Mon, 17 Jan 2011 23:18:46 -0500 Subject: Add rules to database, cast refresh message and trickle down to firewall driver. --- nova/api/ec2/admin.py | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index b784c5a00..1ae5f7094 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -21,7 +21,10 @@ Admin API controller, exposed through http via the api worker. """ import base64 +import IPy +import urllib +from nova import compute from nova import db from nova import exception from nova import log as logging @@ -70,6 +73,9 @@ class AdminController(object): def __str__(self): return 'AdminController' + def __init__(self): + self.compute_api = compute.API() + def describe_user(self, _context, name, **_kwargs): """Returns user data, including access and secret keys.""" return user_dict(manager.AuthManager().get_user(name)) @@ -210,10 +216,39 @@ class AdminController(object): """Returns status info for single node.""" return host_dict(db.host_get(name)) + def _provider_fw_rule_exists(context, rule): + for old_rule in db.provider_fw_rule_get_all(context): + for key in ('cidr', 'from_port', 'to_port', 'protocol'): + dupe = True + if rule[key] != old_rule[key]: + dupe = False + if dupe: + return dupe + return False + + def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" - LOG.audit(_("Blocking access to all projects incoming from %s"), + LOG.audit(_("Blocking traffic to all projects incoming from %s"), cidr, context=context) - raise NotImplementedError(_("Awaiting implementation.")) - # TODO(todd): implement - # return {'status': 'OK', 'message': 'Disabled (number) IPs'} + rule = {'cidr': IPy.IP(urllib.unquote(cidr).decode())} + tcp_rule = rule.copy() + tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) + udp_rule = rule.copy() + udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) + icmp_rule = rule.copy() + icmp_rule.update({"protocol": "ICMP", "from_port": -1, "to_port": -1}) + rules_added = 0 + if not self._provider_fw_rule_exists(context, tcp_rule): + db.provider_fw_rule_create(context, tcp_rule) + rules_added += 1 + if not self._provider_fw_rule_exists(context, udp_rule): + db.provider_fw_rule_create(context, udp_rule) + rules_added += 1 + if not self._provider_fw_rule_exists(context, icmp_rule): + db.provider_fw_rule_create(context, icmp_rule) + rules_added += 1 + if rules_added == 0: + raise exception.ApiError(_('Duplicate rule')) + self.compute_api.trigger_provider_fw_rules_refresh(context) + return {'status': 'OK', 'message': 'Disabled (number) IPs'} -- cgit From 46c1c554e7d98959a2b20597d6b0f2b0f648cdc9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 19 Jan 2011 15:16:12 -0500 Subject: Whitespace (pep8) cleanups. --- nova/api/ec2/admin.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 1ae5f7094..3a8ed39eb 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -226,7 +226,6 @@ class AdminController(object): return dupe return False - def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" LOG.audit(_("Blocking traffic to all projects incoming from %s"), -- cgit From f02c9e781bdddd609601da81b97a438b6d5b9781 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 21 Jan 2011 12:30:26 -0500 Subject: Add provider_fw_rules awareness to iptables firewall driver. --- nova/api/ec2/admin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 3a8ed39eb..c83f84b15 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -236,7 +236,8 @@ class AdminController(object): udp_rule = rule.copy() udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "ICMP", "from_port": -1, "to_port": -1}) + icmp_rule.update({"protocol": "ICMP", "from_port": -1, + "to_port": None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): db.provider_fw_rule_create(context, tcp_rule) -- cgit From 4e3524c57f6fa0f917bdb30ec15c8d4633a307e5 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 25 Jan 2011 12:52:00 -0800 Subject: Updates for provider_fw_rules in admin api. --- nova/api/ec2/admin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 4a34476d3..0dabf2092 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -223,7 +223,7 @@ class AdminController(object): """Returns status info for single node.""" return host_dict(db.host_get(name)) - def _provider_fw_rule_exists(context, rule): + def _provider_fw_rule_exists(self, context, rule): for old_rule in db.provider_fw_rule_get_all(context): for key in ('cidr', 'from_port', 'to_port', 'protocol'): dupe = True @@ -237,7 +237,10 @@ class AdminController(object): """Add provider-level firewall rules to block incoming traffic.""" LOG.audit(_("Blocking traffic to all projects incoming from %s"), cidr, context=context) - rule = {'cidr': IPy.IP(urllib.unquote(cidr).decode())} + cidr = urllib.unquote(cidr).decode() + # raise if invalid + IPy.IP(cidr) + rule = {'cidr': cidr} tcp_rule = rule.copy() tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) udp_rule = rule.copy() -- cgit From d6c6d8115b9dda07716d85fb1201cde0e907a9bd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 26 Jan 2011 22:54:39 -0800 Subject: A couple of bugfixes. --- nova/api/ec2/admin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 478032ce9..b019e8e8b 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -237,9 +237,10 @@ class AdminController(object): return host_dict(db.host_get(name)) def _provider_fw_rule_exists(self, context, rule): + # TODO(todd): we call this repeatedly, can we filter by protocol? for old_rule in db.provider_fw_rule_get_all(context): + dupe = True for key in ('cidr', 'from_port', 'to_port', 'protocol'): - dupe = True if rule[key] != old_rule[key]: dupe = False if dupe: -- cgit From 2b79fa82872c55368167fc7433cb28a2369f5191 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 7 Apr 2011 01:42:49 -0400 Subject: test provider fw rules at the virt/ipteables layer. lowercase protocol names in admin api to match what the firewall driver expects. add provider fw rule chain in iptables6 as well. fix a couple of small typos and copy-paste errors. --- nova/api/ec2/admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 0b27854ef..c0c2bcd0d 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -356,11 +356,11 @@ class AdminController(object): IPy.IP(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() - tcp_rule.update({"protocol": "TCP", "from_port": 1, "to_port": 65535}) + tcp_rule.update({"protocol": "tcp", "from_port": 1, "to_port": 65535}) udp_rule = rule.copy() - udp_rule.update({"protocol": "UDP", "from_port": 1, "to_port": 65535}) + udp_rule.update({"protocol": "udp", "from_port": 1, "to_port": 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "ICMP", "from_port": -1, + icmp_rule.update({"protocol": "icmp", "from_port": -1, "to_port": None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): -- cgit From 0be9e06c09c1d08802a8963e34090b5fcedb19be Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 11:35:58 -0400 Subject: Double quotes are ugly. --- nova/api/ec2/admin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 0d08aba7a..3c7a60277 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -349,19 +349,19 @@ class AdminController(object): def block_external_addresses(self, context, cidr): """Add provider-level firewall rules to block incoming traffic.""" - LOG.audit(_("Blocking traffic to all projects incoming from %s"), + LOG.audit(_('Blocking traffic to all projects incoming from %s'), cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid IPy.IP(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() - tcp_rule.update({"protocol": "tcp", "from_port": 1, "to_port": 65535}) + tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535}) udp_rule = rule.copy() - udp_rule.update({"protocol": "udp", "from_port": 1, "to_port": 65535}) + udp_rule.update({'protocol': 'udp', 'from_port': 1, 'to_port': 65535}) icmp_rule = rule.copy() - icmp_rule.update({"protocol": "icmp", "from_port": -1, - "to_port": None}) + icmp_rule.update({'protocol': 'icmp', 'from_port': -1, + 'to_port': None}) rules_added = 0 if not self._provider_fw_rule_exists(context, tcp_rule): db.provider_fw_rule_create(context, tcp_rule) @@ -375,4 +375,4 @@ class AdminController(object): if rules_added == 0: raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) - return {'status': 'OK', 'message': 'Disabled (number) IPs'} + return {'status': 'OK', 'message': 'Added %s rules' % rules_added} -- cgit From 1fd3f5a6edf911dc84e11130bc2c590567d780c3 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 26 May 2011 17:05:55 -0400 Subject: Fixes from Ed Leafe's review suggestions. --- nova/api/ec2/admin.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 3c7a60277..1d4cffff3 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -339,12 +339,9 @@ class AdminController(object): def _provider_fw_rule_exists(self, context, rule): # TODO(todd): we call this repeatedly, can we filter by protocol? for old_rule in db.provider_fw_rule_get_all(context): - dupe = True - for key in ('cidr', 'from_port', 'to_port', 'protocol'): - if rule[key] != old_rule[key]: - dupe = False - if dupe: - return dupe + if all([rule[k] == old_rule[k] for k in ('cidr', 'from_port', + 'to_port', 'protocol')]): + return True return False def block_external_addresses(self, context, cidr): @@ -372,7 +369,7 @@ class AdminController(object): if not self._provider_fw_rule_exists(context, icmp_rule): db.provider_fw_rule_create(context, icmp_rule) rules_added += 1 - if rules_added == 0: + if not rules_added: raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) return {'status': 'OK', 'message': 'Added %s rules' % rules_added} -- cgit From 93f61c18134e6b4091114a7126f52d74e0d20df8 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 6 Jun 2011 15:33:35 -0400 Subject: Remove ipy from nova/api/ec2/cloud.py and use netaddr --- nova/api/ec2/cloud.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index b7a9a8633..ceeaa3e48 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -23,7 +23,7 @@ datastore. """ import base64 -import IPy +import netaddr import os import urllib import tempfile @@ -444,7 +444,7 @@ class CloudController(object): elif cidr_ip: # If this fails, it throws an exception. This is what we want. cidr_ip = urllib.unquote(cidr_ip).decode() - IPy.IP(cidr_ip) + netaddr.IPNetwork(cidr_ip) values['cidr'] = cidr_ip else: values['cidr'] = '0.0.0.0/0' -- cgit From 781f3f07ebc3236404e33189e0a76cbb877dff18 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 10 Jun 2011 16:35:35 -0400 Subject: adding xml support to /images//meta resource; moving show/update entities into meta container --- nova/api/openstack/image_metadata.py | 58 ++++++++++++++++++++++++++++++++---- nova/api/openstack/wsgi.py | 8 +++-- 2 files changed, 58 insertions(+), 8 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index ebfe2bde9..77028be28 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -16,6 +16,7 @@ # under the License. from webob import exc +from xml.dom import minidom from nova import flags from nova import image @@ -59,7 +60,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {id: metadata[id]} + return {'meta': {id: metadata[id]}} else: return faults.Fault(exc.HTTPNotFound()) @@ -77,15 +78,15 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body: + if not id in body['meta']: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: + if len(body['meta']) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body[id] + metadata[id] = body['meta'][id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) @@ -103,9 +104,56 @@ class Controller(object): self.image_service.update(context, image_id, img, None) +class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): + def __init__(self): + xmlns = wsgi.XMLNS_V11 + super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns) + + def _meta_item_to_xml(self, doc, key, value): + node = doc.createElement('meta') + node.setAttribute('key', key) + text = doc.createTextNode(value) + node.appendChild(text) + return node + + def _meta_list_to_xml(self, xml_doc, meta_items): + container_node = xml_doc.createElement('metadata') + for (key, value) in meta_items: + item_node = self._meta_item_to_xml(xml_doc, key, value) + container_node.appendChild(item_node) + return container_node + + def _meta_list_to_xml_string(self, metadata_dict): + xml_doc = minidom.Document() + items = metadata_dict['metadata'].items() + container_node = self._meta_list_to_xml(xml_doc, items) + self._add_xmlns(container_node) + return container_node.toprettyxml(indent=' ') + + def index(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def create(self, metadata_dict): + return self._meta_list_to_xml_string(metadata_dict) + + def _meta_item_to_xml_string(self, meta_item_dict): + xml_doc = minidom.Document() + item_key, item_value = meta_item_dict.items()[0] + item_node = self._meta_item_to_xml(xml_doc, item_key, item_value) + self._add_xmlns(item_node) + return item_node.toprettyxml(indent=' ') + + def show(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + def update(self, meta_item_dict): + return self._meta_item_to_xml_string(meta_item_dict['meta']) + + + def create_resource(): serializers = { - 'application/xml': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V11), + 'application/xml': ImageMetadataXMLSerializer(), } return wsgi.Resource(Controller(), serializers=serializers) diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index ddf4e6fa9..d1402b314 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -221,12 +221,14 @@ class XMLDictSerializer(DictSerializer): doc = minidom.Document() node = self._to_xml_node(doc, self.metadata, root_key, data[root_key]) - xmlns = node.getAttribute('xmlns') - if not xmlns and self.xmlns: - node.setAttribute('xmlns', self.xmlns) + self._add_xmlns(node) return node.toprettyxml(indent=' ') + def _add_xmlns(self, node): + if self.xmlns is not None: + node.setAttribute('xmlns', self.xmlns) + def _to_xml_node(self, doc, metadata, nodename, data): """Recursive method to convert data members to XML nodes.""" result = doc.createElement(nodename) -- cgit From d1b6ebb4009e13ac2cf2309275a66a634e4f9171 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 16:42:58 -0400 Subject: Add ability to list ip blocks. --- nova/api/ec2/admin.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index fcf7f674c..37ae2e648 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -369,3 +369,11 @@ class AdminController(object): raise exception.ApiError(_('Duplicate rule')) self.compute_api.trigger_provider_fw_rules_refresh(context) return {'status': 'OK', 'message': 'Added %s rules' % rules_added} + + def describe_external_address_blocks(self, context): + blocks = db.provider_fw_rule_get_all(context) + # NOTE(todd): use a set since we have icmp/udp/tcp rules with same cidr + blocks = set([b.cidr for b in blocks]) + blocks = [{'cidr': b} for b in blocks] + return {'externalIpBlockInfo': + list(sorted(blocks, key=lambda k: k['cidr']))} -- cgit From ed3914eafa7d076fdcc03ee958f77528bcf20603 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 11 Jun 2011 18:03:45 -0400 Subject: Add a method to delete provider firewall rules. --- nova/api/ec2/admin.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index 37ae2e648..da982d8dc 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -377,3 +377,15 @@ class AdminController(object): blocks = [{'cidr': b} for b in blocks] return {'externalIpBlockInfo': list(sorted(blocks, key=lambda k: k['cidr']))} + + def remove_external_address_block(self, context, cidr): + LOG.audit(_('Removing ip block from %s'), cidr, context=context) + cidr = urllib.unquote(cidr).decode() + # raise if invalid + IPy.IP(cidr) + rules = db.provider_fw_rule_get_all_by_cidr(context, cidr) + for rule in rules: + db.provider_fw_rule_destroy(context, rule['id']) + if rules: + self.compute_api.trigger_provider_fw_rules_refresh(context) + return {'status': 'OK', 'message': 'Deleted %s rules' % len(rules)} -- cgit From 7a2712ebf74e5565663a6723a992151f71255eff Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 14 Jun 2011 11:34:03 -0700 Subject: Move ipy commands to netaddr. --- nova/api/ec2/admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index fcf7f674c..343bc61c4 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -22,7 +22,7 @@ Admin API controller, exposed through http via the api worker. import base64 import datetime -import IPy +import netaddr import urllib from nova import compute @@ -346,7 +346,7 @@ class AdminController(object): cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid - IPy.IP(cidr) + netaddr.IPNetwork(cidr) rule = {'cidr': cidr} tcp_rule = rule.copy() tcp_rule.update({'protocol': 'tcp', 'from_port': 1, 'to_port': 65535}) -- cgit From 25009974df913c3e5c071b53a6004ae35e37d26b Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Thu, 16 Jun 2011 17:37:45 +0400 Subject: First implementation of FloatingIpController --- nova/api/openstack/contrib/floating_ips.py | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 nova/api/openstack/contrib/floating_ips.py (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py new file mode 100644 index 000000000..b67b8b4ec --- /dev/null +++ b/nova/api/openstack/contrib/floating_ips.py @@ -0,0 +1,54 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# Copyright 2011 Grid Dynamics +# +# 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 webob import exc + +from nova import exception +from nova import network +from nova.api.openstack import faults + +def _translate_floating_ip_detail_view(context, floating_ip): + #TODO(enugaev) implement view + return None + +class FloatingIPController(object): + """The Volumes API controller for the OpenStack API.""" + + _serialization_metadata = { + 'application/xml': { + "attributes": { + "floating_ip": [ + "id", + "ip", + "instance_id", + "fixed_ip", + ]}}} + + def __init__(self): + self.network_api = network.API() + super(FloatingIPController, self).__init__() + + def show(self, req, id): + """Return data about the given volume.""" + context = req.environ['nova.context'] + + try: + floating_ip = self.network_api.get(context, id) + except exception.NotFound: + return faults.Fault(exc.HTTPNotFound()) + + return {'volume': _translate_floating_ip_detail_view(context, + floating_ip)} \ No newline at end of file -- cgit From a1e310aaa9f0ef829e2857c524be140541f3a13d Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Thu, 16 Jun 2011 20:31:09 +0400 Subject: floating_ips extension is loading to api now --- nova/api/openstack/contrib/floating_ips.py | 45 ++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index b67b8b4ec..6c08f52e5 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -19,11 +19,16 @@ from webob import exc from nova import exception from nova import network from nova.api.openstack import faults +from nova.api.openstack import extensions def _translate_floating_ip_detail_view(context, floating_ip): #TODO(enugaev) implement view return None +def _translate_floating_ips_view(context, floating_ips): + #TODO(adiantum) implement view + return [] + class FloatingIPController(object): """The Volumes API controller for the OpenStack API.""" @@ -50,5 +55,41 @@ class FloatingIPController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'volume': _translate_floating_ip_detail_view(context, - floating_ip)} \ No newline at end of file + return {'floating_ips': _translate_floating_ip_detail_view(context, + floating_ip)} + + def index(self, req): + context = req.environ['nova.context'] + + floating_ips = self.network_api.list(context) + + return {'floating_ips' : _translate_floating_ips_view(context, + floating_ips)} + + +class Floating_ips(extensions.ExtensionDescriptor): + def get_name(self): + return "Floating_ips" + + def get_alias(self): + return "FLOATING_IPS" + + def get_description(self): + return "Floating IPs support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/floating_ips/api/v1.1" + + def get_updated(self): + return "2011-06-16T00:00:00+00:00" + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension('floating_ips', + FloatingIPController(), + collection_actions={}) + resources.append(res) + + return resources + -- cgit From 971efd1b5568c324c91e826fc347c49ceea3790c Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Fri, 17 Jun 2011 17:27:06 +0400 Subject: stub api methods --- nova/api/openstack/contrib/floating_ips.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 6c08f52e5..f7a939ddd 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -66,6 +66,25 @@ class FloatingIPController(object): return {'floating_ips' : _translate_floating_ips_view(context, floating_ips)} + def create(self, req, body): + context = req.environ['nova.context'] + + return {'allocate': None} + + def delete(self,req, id): + context = req.environ['nova.context'] + + return {'release': None } + + def associate(self, req, id, body): + context = req.environ['nova.context'] + + return {'associate': None} + + def disassociate(self, req, id, body): + context = req.environ['nova.context'] + + return {'disassociate': None} class Floating_ips(extensions.ExtensionDescriptor): def get_name(self): @@ -88,7 +107,9 @@ class Floating_ips(extensions.ExtensionDescriptor): res = extensions.ResourceExtension('floating_ips', FloatingIPController(), - collection_actions={}) + member_actions={ + 'associate' : 'POST', + 'disassociate' : 'POST'}) resources.append(res) return resources -- cgit From bfbb2b8e04d1cd4b761c67973b173d2ca6f84859 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 17 Jun 2011 13:39:34 -0400 Subject: adding extra image service properties to compute api snapshot; adding instance_ref property --- nova/api/openstack/images.py | 31 ++++++++++++++++++++++++++++--- nova/api/openstack/views/images.py | 30 +++++++++++++----------------- 2 files changed, 41 insertions(+), 20 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 5ffd8e96a..4a09060c9 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import os.path + import webob.exc from nova import compute @@ -104,7 +106,10 @@ class Controller(object): except KeyError: raise webob.exc.HTTPBadRequest() - image = self._compute_service.snapshot(context, server_id, image_name) + props = self._get_extra_properties(req, body) + + image = self._compute_service.snapshot(context, server_id, + image_name, props) return dict(image=self.get_builder(req).build(image, detail=True)) def get_builder(self, request): @@ -114,6 +119,9 @@ class Controller(object): def _server_id_from_req_data(self, data): raise NotImplementedError() + def _get_extra_properties(self, req, data): + return {} + class ControllerV10(Controller): """Version 1.0 specific controller logic.""" @@ -150,7 +158,11 @@ class ControllerV10(Controller): return dict(images=[builder(image, detail=True) for image in images]) def _server_id_from_req_data(self, data): - return data['image']['serverId'] + try: + return data['image']['serverId'] + except KeyError: + msg = _("Expected serverId attribute on server entity.") + raise webob.exc.HTTPBadRequest(explanation=msg) class ControllerV11(Controller): @@ -190,7 +202,20 @@ class ControllerV11(Controller): return dict(images=[builder(image, detail=True) for image in images]) def _server_id_from_req_data(self, data): - return data['image']['serverRef'] + try: + server_ref = data['image']['serverRef'] + except KeyError: + msg = _("Expected serverRef attribute on server entity.") + raise webob.exc.HTTPBadRequest(explanation=msg) + + return os.path.split(server_ref)[1] + + def _get_extra_properties(self, req, data): + server_ref = data['image']['serverRef'] + if not server_ref.startswith('http'): + server_ref = os.path.join(req.application_url, 'servers', + server_ref) + return {'instance_ref': server_ref} def create_resource(version='1.0'): diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 2773c9c13..d6a054102 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -46,13 +46,9 @@ class ViewBuilder(object): except KeyError: image['status'] = image['status'].upper() - def _build_server(self, image, instance_id): + def _build_server(self, image, image_obj): """Indicates that you must use a ViewBuilder subclass.""" - raise NotImplementedError - - def generate_server_ref(self, server_id): - """Return an href string pointing to this server.""" - return os.path.join(self._url, "servers", str(server_id)) + raise NotImplementedError() def generate_href(self, image_id): """Return an href string pointing to this object.""" @@ -60,8 +56,6 @@ class ViewBuilder(object): def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" - properties = image_obj.get("properties", {}) - self._format_dates(image_obj) if "status" in image_obj: @@ -72,11 +66,7 @@ class ViewBuilder(object): "name": image_obj.get("name"), } - if "instance_id" in properties: - try: - self._build_server(image, int(properties["instance_id"])) - except ValueError: - pass + self._build_server(image, image_obj) if detail: image.update({ @@ -94,15 +84,21 @@ class ViewBuilder(object): class ViewBuilderV10(ViewBuilder): """OpenStack API v1.0 Image Builder""" - def _build_server(self, image, instance_id): - image["serverId"] = instance_id + def _build_server(self, image, image_obj): + try: + image['serverId'] = int(image_obj['properties']['instance_id']) + except (KeyError, ValueError): + pass class ViewBuilderV11(ViewBuilder): """OpenStack API v1.1 Image Builder""" - def _build_server(self, image, instance_id): - image["serverRef"] = self.generate_server_ref(instance_id) + def _build_server(self, image, image_obj): + try: + image['serverRef'] = image_obj['properties']['instance_ref'] + except KeyError: + return def build(self, image_obj, detail=False): """Return a standardized image structure for display by the API.""" -- cgit From 2ee267b7e463b3f0b7997f5dce91b325610795ab Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Fri, 17 Jun 2011 14:35:10 -0400 Subject: adding check for serverRef hostname matching app url --- nova/api/openstack/images.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py index 4a09060c9..d43340e10 100644 --- a/nova/api/openstack/images.py +++ b/nova/api/openstack/images.py @@ -101,7 +101,7 @@ class Controller(object): raise webob.exc.HTTPBadRequest() try: - server_id = self._server_id_from_req_data(body) + server_id = self._server_id_from_req(req, body) image_name = body["image"]["name"] except KeyError: raise webob.exc.HTTPBadRequest() @@ -116,7 +116,7 @@ class Controller(object): """Indicates that you must use a Controller subclass.""" raise NotImplementedError - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): raise NotImplementedError() def _get_extra_properties(self, req, data): @@ -157,7 +157,7 @@ class ControllerV10(Controller): builder = self.get_builder(req).build return dict(images=[builder(image, detail=True) for image in images]) - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): try: return data['image']['serverId'] except KeyError: @@ -201,14 +201,20 @@ class ControllerV11(Controller): builder = self.get_builder(req).build return dict(images=[builder(image, detail=True) for image in images]) - def _server_id_from_req_data(self, data): + def _server_id_from_req(self, req, data): try: server_ref = data['image']['serverRef'] except KeyError: msg = _("Expected serverRef attribute on server entity.") raise webob.exc.HTTPBadRequest(explanation=msg) - return os.path.split(server_ref)[1] + head, tail = os.path.split(server_ref) + + if head and head != os.path.join(req.application_url, 'servers'): + msg = _("serverRef must match request url") + raise webob.exc.HTTPBadRequest(explanation=msg) + + return tail def _get_extra_properties(self, req, data): server_ref = data['image']['serverRef'] -- cgit From 7398819cc00a078a486b4d2f11846ff32db19a88 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 17:07:26 -0400 Subject: moving image show/update into 'meta' container --- nova/api/openstack/image_metadata.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index ebfe2bde9..07f0fb4fb 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -59,7 +59,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {id: metadata[id]} + return {'meta': {id: metadata[id]}} else: return faults.Fault(exc.HTTPNotFound()) @@ -77,15 +77,22 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body: + + try: + meta = body['meta'] + except KeyError: + expl = _('Incorrect request body format') + raise exc.HTTPBadRequest(explanation=expl) + + if not id in meta: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: + if len(meta) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body[id] + metadata[id] = meta[id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) -- cgit From 1f9cd3e7c97034408b5afe3fc3720c48040dea97 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 17:14:58 -0400 Subject: reverting non-xml changes --- nova/api/openstack/image_metadata.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 77028be28..399cd2637 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -60,7 +60,7 @@ class Controller(object): context = req.environ['nova.context'] metadata = self._get_metadata(context, image_id) if id in metadata: - return {'meta': {id: metadata[id]}} + return {id: metadata[id]} else: return faults.Fault(exc.HTTPNotFound()) @@ -78,15 +78,15 @@ class Controller(object): def update(self, req, image_id, id, body): context = req.environ['nova.context'] - if not id in body['meta']: + if not id in body: expl = _('Request body and URI mismatch') raise exc.HTTPBadRequest(explanation=expl) - if len(body['meta']) > 1: + if len(body) > 1: expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) img = self.image_service.show(context, image_id) metadata = self._get_metadata(context, image_id, img) - metadata[id] = body['meta'][id] + metadata[id] = body[id] self._check_quota_limit(context, metadata) img['properties'] = metadata self.image_service.update(context, image_id, img, None) -- cgit From 548ac151cd1c7de5249fdeb651895917e83df488 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Wed, 22 Jun 2011 18:22:57 -0400 Subject: pep8 fixes --- nova/api/openstack/image_metadata.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/image_metadata.py b/nova/api/openstack/image_metadata.py index 399cd2637..691264553 100644 --- a/nova/api/openstack/image_metadata.py +++ b/nova/api/openstack/image_metadata.py @@ -150,7 +150,6 @@ class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer): return self._meta_item_to_xml_string(meta_item_dict['meta']) - def create_resource(): serializers = { 'application/xml': ImageMetadataXMLSerializer(), -- cgit From b7684e0d36010050ce8254bbbf4573a6a624fa69 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Thu, 23 Jun 2011 04:28:42 +0400 Subject: allocate and release implementation --- nova/api/openstack/contrib/floating_ips.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index f7a939ddd..9bad6806c 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -18,6 +18,7 @@ from webob import exc from nova import exception from nova import network +from nova import rpc from nova.api.openstack import faults from nova.api.openstack import extensions @@ -69,12 +70,27 @@ class FloatingIPController(object): def create(self, req, body): context = req.environ['nova.context'] - return {'allocate': None} + try: + ip = self.network_api.allocate_floating_ip(context) + except rpc.RemoteError as ex: + if ex.exc_type == 'NoMoreAddresses': + raise exception.NoMoreFloatingIps() + else: + raise + + return {'allocated': ip} def delete(self,req, id): context = req.environ['nova.context'] - return {'release': None } + if id.isdigit(): + ip = self.network_api.get(id) + else: + ip = id + + self.network_api.release_floating_ip(context, address=ip) + + return {'released': ip } def associate(self, req, id, body): context = req.environ['nova.context'] -- cgit From a9de2c26432b0b6c77e941db0199fd72a54e2d69 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Wed, 22 Jun 2011 22:40:00 -0400 Subject: Added flavor extra specs controller --- nova/api/openstack/__init__.py | 6 ++ nova/api/openstack/flavor_extra_specs.py | 106 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 nova/api/openstack/flavor_extra_specs.py (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index ddd9580d7..464ba1a9a 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -32,6 +32,7 @@ from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import consoles from nova.api.openstack import flavors +from nova.api.openstack import flavor_extra_specs from nova.api.openstack import images from nova.api.openstack import image_metadata from nova.api.openstack import ips @@ -178,3 +179,8 @@ class APIRouterV11(APIRouter): controller=server_metadata.create_resource(), parent_resource=dict(member_name='server', collection_name='servers')) + + mapper.resource("flavor_extra_specs", "extra", + controller=flavor_extra_specs.create_resource(), + parent_resource=dict(member_name='flavor', + collection_name='flavors')) diff --git a/nova/api/openstack/flavor_extra_specs.py b/nova/api/openstack/flavor_extra_specs.py new file mode 100644 index 000000000..2dcd85688 --- /dev/null +++ b/nova/api/openstack/flavor_extra_specs.py @@ -0,0 +1,106 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 University of Southern California +# 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 webob import exc + +from nova import db +from nova import quota +from nova.api.openstack import faults +from nova.api.openstack import wsgi + + +class Controller(object): + """ The flavor extra specs API controller for the Openstack API """ + + def __init__(self): + self.compute_api = compute.API() + super(Controller, self).__init__() + + def _get_extra_specs(self, context, flavor_id): + extra_specs = self.db.instance_type_extra_specs_get(context, flavor_id) + specs_dict = {} + for key, value in specs.iteritems(): + specs_dict[key] = value + return dict(extra=specs_dict) + + def _check_body(self, body): + if body == None or body == "": + expl = _('No Request Body') + raise exc.HTTPBadRequest(explanation=expl) + + def index(self, req, flavor_id): + """ Returns the list of extra specs for a givenflavor """ + context = req.environ['nova.context'] + return self._get_extra_specs(context, flavor_id) + + def create(self, req, flavor_id, body): + self._check_body(body) + context = req.environ['nova.context'] + specs = body.get('extra') + try: + self.db.instance_type_extra_specs_update_or_create(context, + flavor_id, + specs) + except quota.QuotaError as error: + self._handle_quota_error(error) + return body + + def update(self, req, flavor_id, id, body): + self._check_body(body) + context = req.environ['nova.context'] + if not id in body: + expl = _('Request body and URI mismatch') + raise exc.HTTPBadRequest(explanation=expl) + if len(body) > 1: + expl = _('Request body contains too many items') + raise exc.HTTPBadRequest(explanation=expl) + try: + self.db.instance_type_extra_specs_update_or_create(context, + flavor_id, + body) + except quota.QuotaError as error: + self._handle_quota_error(error) + + return body + + def show(self, req, flavor_id, id): + """ Return a single extra spec item """ + context = req.environ['nova.context'] + specs = self._get_extra_specs(context, flavor_id) + if id in specs['extra']: + return {id: specs['extra'][id]} + else: + return faults.Fault(exc.HTTPNotFound()) + + def delete(self, req, flavor_id, id): + """ Deletes an existing extra spec """ + context = req.environ['nova.context'] + self.instance_type_extra_specs_delete(context, flavor_id, id) + + def _handle_quota_error(self, error): + """Reraise quota errors as api-specific http exceptions.""" + if error.code == "MetadataLimitExceeded": + raise exc.HTTPBadRequest(explanation=error.message) + raise error + + +def create_resource(): + serializers = { + 'application/xml': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V11), + } + + return wsgi.Resource(Controller(), serializers=serializers) -- cgit From 173bb3c54b7ce9874f6bf880a5df8966fd508c38 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Wed, 22 Jun 2011 22:43:44 -0400 Subject: Bug fixing --- nova/api/openstack/flavor_extra_specs.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/flavor_extra_specs.py b/nova/api/openstack/flavor_extra_specs.py index 2dcd85688..d14bed813 100644 --- a/nova/api/openstack/flavor_extra_specs.py +++ b/nova/api/openstack/flavor_extra_specs.py @@ -26,14 +26,10 @@ from nova.api.openstack import wsgi class Controller(object): """ The flavor extra specs API controller for the Openstack API """ - def __init__(self): - self.compute_api = compute.API() - super(Controller, self).__init__() - def _get_extra_specs(self, context, flavor_id): - extra_specs = self.db.instance_type_extra_specs_get(context, flavor_id) + extra_specs = db.instance_type_extra_specs_get(context, flavor_id) specs_dict = {} - for key, value in specs.iteritems(): + for key, value in extra_specs.iteritems(): specs_dict[key] = value return dict(extra=specs_dict) -- cgit From 6afcabac7442aa2e3944a3fef3d3452c189c1901 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Wed, 22 Jun 2011 23:14:39 -0400 Subject: Now passing unit tests --- nova/api/openstack/flavor_extra_specs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/flavor_extra_specs.py b/nova/api/openstack/flavor_extra_specs.py index d14bed813..6a6d2f7a1 100644 --- a/nova/api/openstack/flavor_extra_specs.py +++ b/nova/api/openstack/flavor_extra_specs.py @@ -27,7 +27,7 @@ class Controller(object): """ The flavor extra specs API controller for the Openstack API """ def _get_extra_specs(self, context, flavor_id): - extra_specs = db.instance_type_extra_specs_get(context, flavor_id) + extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id) specs_dict = {} for key, value in extra_specs.iteritems(): specs_dict[key] = value @@ -48,9 +48,9 @@ class Controller(object): context = req.environ['nova.context'] specs = body.get('extra') try: - self.db.instance_type_extra_specs_update_or_create(context, - flavor_id, - specs) + db.api.instance_type_extra_specs_update_or_create(context, + flavor_id, + specs) except quota.QuotaError as error: self._handle_quota_error(error) return body @@ -65,7 +65,7 @@ class Controller(object): expl = _('Request body contains too many items') raise exc.HTTPBadRequest(explanation=expl) try: - self.db.instance_type_extra_specs_update_or_create(context, + db.api.instance_type_extra_specs_update_or_create(context, flavor_id, body) except quota.QuotaError as error: @@ -85,7 +85,7 @@ class Controller(object): def delete(self, req, flavor_id, id): """ Deletes an existing extra spec """ context = req.environ['nova.context'] - self.instance_type_extra_specs_delete(context, flavor_id, id) + db.api.instance_type_extra_specs_delete(context, flavor_id, id) def _handle_quota_error(self, error): """Reraise quota errors as api-specific http exceptions.""" -- cgit From 9a6e9a1af9359fb4a9261f59f57113f252f0d6e9 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 23 Jun 2011 14:45:37 -0400 Subject: Make firewall rules tests idempotent, move IPy=>netaddr, add deltete test. --- nova/api/ec2/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py index b8fc8f114..df7876b9d 100644 --- a/nova/api/ec2/admin.py +++ b/nova/api/ec2/admin.py @@ -382,7 +382,7 @@ class AdminController(object): LOG.audit(_('Removing ip block from %s'), cidr, context=context) cidr = urllib.unquote(cidr).decode() # raise if invalid - IPy.IP(cidr) + netaddr.IPNetwork(cidr) rules = db.provider_fw_rule_get_all_by_cidr(context, cidr) for rule in rules: db.provider_fw_rule_destroy(context, rule['id']) -- cgit From c2216547d0c55e32a4f8203129f4604f4ba004c7 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Fri, 24 Jun 2011 00:39:37 +0400 Subject: Implemented view and added tests --- nova/api/openstack/contrib/floating_ips.py | 57 ++++++++++++++++++------------ 1 file changed, 35 insertions(+), 22 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 9bad6806c..dc048869c 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -22,13 +22,25 @@ from nova import rpc from nova.api.openstack import faults from nova.api.openstack import extensions -def _translate_floating_ip_detail_view(context, floating_ip): - #TODO(enugaev) implement view - return None -def _translate_floating_ips_view(context, floating_ips): - #TODO(adiantum) implement view - return [] +def _translate_floating_ip_view(floating_ip): + result = {'id': floating_ip['id'], + 'ip': floating_ip['address']} + if 'fixed_ip' in floating_ip: + result['fixed_ip'] = floating_ip['fixed_ip']['address'] + else: + result['fixed_ip'] = None + if 'instance' in floating_ip: + result['instance_id'] = floating_ip['instance']['id'] + else: + result['instance_id'] = None + return {'floating_ip': result} + + +def _translate_floating_ips_view(floating_ips): + return {'floating_ips': [_translate_floating_ip_view(floating_ip) + for floating_ip in floating_ips]} + class FloatingIPController(object): """The Volumes API controller for the OpenStack API.""" @@ -48,7 +60,7 @@ class FloatingIPController(object): super(FloatingIPController, self).__init__() def show(self, req, id): - """Return data about the given volume.""" + """Return data about the given floating ip.""" context = req.environ['nova.context'] try: @@ -56,16 +68,14 @@ class FloatingIPController(object): except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) - return {'floating_ips': _translate_floating_ip_detail_view(context, - floating_ip)} + return _translate_floating_ip_view(floating_ip) def index(self, req): context = req.environ['nova.context'] floating_ips = self.network_api.list(context) - return {'floating_ips' : _translate_floating_ips_view(context, - floating_ips)} + return _translate_floating_ips_view(floating_ips) def create(self, req, body): context = req.environ['nova.context'] @@ -80,17 +90,13 @@ class FloatingIPController(object): return {'allocated': ip} - def delete(self,req, id): + def delete(self, req, id): context = req.environ['nova.context'] - - if id.isdigit(): - ip = self.network_api.get(id) - else: - ip = id - + + ip = self._get_ip_by_id(context, id) self.network_api.release_floating_ip(context, address=ip) - return {'released': ip } + return {'released': ip} def associate(self, req, id, body): context = req.environ['nova.context'] @@ -102,6 +108,14 @@ class FloatingIPController(object): return {'disassociate': None} + def _get_ip_by_id(self, context, value): + """Checks that value is id and then returns its address. + If value is ip then return value.""" + if value.isdigit(): + return self.network_api.get(context, value)['address'] + return value + + class Floating_ips(extensions.ExtensionDescriptor): def get_name(self): return "Floating_ips" @@ -124,9 +138,8 @@ class Floating_ips(extensions.ExtensionDescriptor): res = extensions.ResourceExtension('floating_ips', FloatingIPController(), member_actions={ - 'associate' : 'POST', - 'disassociate' : 'POST'}) + 'associate': 'POST', + 'disassociate': 'POST'}) resources.append(res) return resources - -- cgit From 188dd9117318cc4f5ebe0be9d19b9737a43ce68b Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Thu, 23 Jun 2011 23:42:44 -0400 Subject: Starting to transition instance type extra specs API to an extension API --- nova/api/openstack/__init__.py | 5 - nova/api/openstack/contrib/flavorextraspecs.py | 123 +++++++++++++++++++++++++ nova/api/openstack/flavor_extra_specs.py | 102 -------------------- 3 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 nova/api/openstack/contrib/flavorextraspecs.py delete mode 100644 nova/api/openstack/flavor_extra_specs.py (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 857a5431b..859cac669 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -178,8 +178,3 @@ class APIRouterV11(APIRouter): controller=server_metadata.create_resource(), parent_resource=dict(member_name='server', collection_name='servers')) - - mapper.resource("flavor_extra_specs", "extra", - controller=flavor_extra_specs.create_resource(), - parent_resource=dict(member_name='flavor', - collection_name='flavors')) diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py new file mode 100644 index 000000000..24c5da7b2 --- /dev/null +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -0,0 +1,123 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 University of Southern California +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" The instance type extra specs extension""" + +from webob import exc + +from nova import db +from nova import quota +from nova.api.openstack import extensions +from nova.api.openstack import faults +from nova.api.openstack import wsgi + + +class FlavorExtraSpecsController(object): + """ The flavor extra specs API controller for the Openstack API """ + + def _get_extra_specs(self, context, flavor_id): + extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id) + specs_dict = {} + for key, value in extra_specs.iteritems(): + specs_dict[key] = value + return dict(extra=specs_dict) + + def _check_body(self, body): + if body == None or body == "": + expl = _('No Request Body') + raise exc.HTTPBadRequest(explanation=expl) + + def index(self, req, flavor_id): + """ Returns the list of extra specs for a givenflavor """ + context = req.environ['nova.context'] + return self._get_extra_specs(context, flavor_id) + + def create(self, req, flavor_id, body): + self._check_body(body) + context = req.environ['nova.context'] + specs = body.get('extra') + try: + db.api.instance_type_extra_specs_update_or_create(context, + flavor_id, + specs) + except quota.QuotaError as error: + self._handle_quota_error(error) + return body + + def update(self, req, flavor_id, id, body): + self._check_body(body) + context = req.environ['nova.context'] + if not id in body: + expl = _('Request body and URI mismatch') + raise exc.HTTPBadRequest(explanation=expl) + if len(body) > 1: + expl = _('Request body contains too many items') + raise exc.HTTPBadRequest(explanation=expl) + try: + db.api.instance_type_extra_specs_update_or_create(context, + flavor_id, + body) + except quota.QuotaError as error: + self._handle_quota_error(error) + + return body + + def show(self, req, flavor_id, id): + """ Return a single extra spec item """ + context = req.environ['nova.context'] + specs = self._get_extra_specs(context, flavor_id) + if id in specs['extra']: + return {id: specs['extra'][id]} + else: + return faults.Fault(exc.HTTPNotFound()) + + def delete(self, req, flavor_id, id): + """ Deletes an existing extra spec """ + context = req.environ['nova.context'] + db.api.instance_type_extra_specs_delete(context, flavor_id, id) + + def _handle_quota_error(self, error): + """Reraise quota errors as api-specific http exceptions.""" + if error.code == "MetadataLimitExceeded": + raise exc.HTTPBadRequest(explanation=error.message) + raise error + +class Flavorextraspecs(extensions.ExtensionDescriptor): + def get_name(self): + return "FlavorExtraSpecs" + + def get_alias(self): + return "flavor-extra-specs" + + def get_description(self): + return "Instance type (flavor) extra specs" + + def get_namespace(self): + return \ + "http://docs.openstack.org/ext/flavor-extra-specs/api/v1.1" + + def get_updated(self): + return "2011-06-23T00:00:00+00:00" + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension('flavor-extra-specs', + FlavorExtraSpecsController()) + resources.append(res) + + return resources diff --git a/nova/api/openstack/flavor_extra_specs.py b/nova/api/openstack/flavor_extra_specs.py deleted file mode 100644 index 6a6d2f7a1..000000000 --- a/nova/api/openstack/flavor_extra_specs.py +++ /dev/null @@ -1,102 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 University of Southern California -# 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 webob import exc - -from nova import db -from nova import quota -from nova.api.openstack import faults -from nova.api.openstack import wsgi - - -class Controller(object): - """ The flavor extra specs API controller for the Openstack API """ - - def _get_extra_specs(self, context, flavor_id): - extra_specs = db.api.instance_type_extra_specs_get(context, flavor_id) - specs_dict = {} - for key, value in extra_specs.iteritems(): - specs_dict[key] = value - return dict(extra=specs_dict) - - def _check_body(self, body): - if body == None or body == "": - expl = _('No Request Body') - raise exc.HTTPBadRequest(explanation=expl) - - def index(self, req, flavor_id): - """ Returns the list of extra specs for a givenflavor """ - context = req.environ['nova.context'] - return self._get_extra_specs(context, flavor_id) - - def create(self, req, flavor_id, body): - self._check_body(body) - context = req.environ['nova.context'] - specs = body.get('extra') - try: - db.api.instance_type_extra_specs_update_or_create(context, - flavor_id, - specs) - except quota.QuotaError as error: - self._handle_quota_error(error) - return body - - def update(self, req, flavor_id, id, body): - self._check_body(body) - context = req.environ['nova.context'] - if not id in body: - expl = _('Request body and URI mismatch') - raise exc.HTTPBadRequest(explanation=expl) - if len(body) > 1: - expl = _('Request body contains too many items') - raise exc.HTTPBadRequest(explanation=expl) - try: - db.api.instance_type_extra_specs_update_or_create(context, - flavor_id, - body) - except quota.QuotaError as error: - self._handle_quota_error(error) - - return body - - def show(self, req, flavor_id, id): - """ Return a single extra spec item """ - context = req.environ['nova.context'] - specs = self._get_extra_specs(context, flavor_id) - if id in specs['extra']: - return {id: specs['extra'][id]} - else: - return faults.Fault(exc.HTTPNotFound()) - - def delete(self, req, flavor_id, id): - """ Deletes an existing extra spec """ - context = req.environ['nova.context'] - db.api.instance_type_extra_specs_delete(context, flavor_id, id) - - def _handle_quota_error(self, error): - """Reraise quota errors as api-specific http exceptions.""" - if error.code == "MetadataLimitExceeded": - raise exc.HTTPBadRequest(explanation=error.message) - raise error - - -def create_resource(): - serializers = { - 'application/xml': wsgi.XMLDictSerializer(xmlns=wsgi.XMLNS_V11), - } - - return wsgi.Resource(Controller(), serializers=serializers) -- cgit From 7a9dc4adc343aa9cf8c21cef741b3bfe409fc41e Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 24 Jun 2011 00:45:53 -0400 Subject: Committing some broken code in advance of trying a different strategy for specifying args to extensions.ResoruceExtensions, using parent --- nova/api/openstack/contrib/flavorextraspecs.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py index 24c5da7b2..f2f740bff 100644 --- a/nova/api/openstack/contrib/flavorextraspecs.py +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -34,7 +34,7 @@ class FlavorExtraSpecsController(object): specs_dict = {} for key, value in extra_specs.iteritems(): specs_dict[key] = value - return dict(extra=specs_dict) + return dict(extra_specs=specs_dict) def _check_body(self, body): if body == None or body == "": @@ -43,13 +43,14 @@ class FlavorExtraSpecsController(object): def index(self, req, flavor_id): """ Returns the list of extra specs for a givenflavor """ + print req.environ context = req.environ['nova.context'] return self._get_extra_specs(context, flavor_id) def create(self, req, flavor_id, body): self._check_body(body) context = req.environ['nova.context'] - specs = body.get('extra') + specs = body.get('extra_specs') try: db.api.instance_type_extra_specs_update_or_create(context, flavor_id, @@ -80,8 +81,8 @@ class FlavorExtraSpecsController(object): """ Return a single extra spec item """ context = req.environ['nova.context'] specs = self._get_extra_specs(context, flavor_id) - if id in specs['extra']: - return {id: specs['extra'][id]} + if id in specs['extra_specs']: + return {id: specs['extra_specs'][id]} else: return faults.Fault(exc.HTTPNotFound()) @@ -108,16 +109,18 @@ class Flavorextraspecs(extensions.ExtensionDescriptor): def get_namespace(self): return \ - "http://docs.openstack.org/ext/flavor-extra-specs/api/v1.1" + "http://docs.openstack.org/ext/flavor_extra_specs/api/v1.1" def get_updated(self): return "2011-06-23T00:00:00+00:00" def get_resources(self): resources = [] - - res = extensions.ResourceExtension('flavor-extra-specs', - FlavorExtraSpecsController()) + res = extensions.ResourceExtension('flavor_extra_specs/:(flavor_id)', + FlavorExtraSpecsController(), + member_actions={ + 'show' : 'flavor_extra_specs/:(flavor_id)/extra/:(id)' }) resources.append(res) - return resources + + -- cgit From 4a0fcd6c1d5540c4bec29ef2585987300654c8b7 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 24 Jun 2011 01:01:30 -0400 Subject: All tests passing --- nova/api/openstack/contrib/flavorextraspecs.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py index f2f740bff..ac67fbf43 100644 --- a/nova/api/openstack/contrib/flavorextraspecs.py +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -43,7 +43,6 @@ class FlavorExtraSpecsController(object): def index(self, req, flavor_id): """ Returns the list of extra specs for a givenflavor """ - print req.environ context = req.environ['nova.context'] return self._get_extra_specs(context, flavor_id) @@ -97,7 +96,9 @@ class FlavorExtraSpecsController(object): raise exc.HTTPBadRequest(explanation=error.message) raise error + class Flavorextraspecs(extensions.ExtensionDescriptor): + def get_name(self): return "FlavorExtraSpecs" @@ -116,11 +117,11 @@ class Flavorextraspecs(extensions.ExtensionDescriptor): def get_resources(self): resources = [] - res = extensions.ResourceExtension('flavor_extra_specs/:(flavor_id)', - FlavorExtraSpecsController(), - member_actions={ - 'show' : 'flavor_extra_specs/:(flavor_id)/extra/:(id)' }) - resources.append(res) - return resources + res = extensions.ResourceExtension( + 'flavor_extra_specs', + FlavorExtraSpecsController(), + parent=dict(member_name='flavor', collection_name='flavors')) + resources.append(res) + return resources -- cgit From 52319f7e4e55e78f4fdd9c76b3ab593322edc875 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 24 Jun 2011 01:02:28 -0400 Subject: Renamed from flavor_extra_specs to extra_specs --- nova/api/openstack/contrib/flavorextraspecs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py index ac67fbf43..7fc8e7954 100644 --- a/nova/api/openstack/contrib/flavorextraspecs.py +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -118,7 +118,7 @@ class Flavorextraspecs(extensions.ExtensionDescriptor): def get_resources(self): resources = [] res = extensions.ResourceExtension( - 'flavor_extra_specs', + 'extra_specs', FlavorExtraSpecsController(), parent=dict(member_name='flavor', collection_name='flavors')) -- cgit From 48f3bccc3372023c35a75671e25e9089dd4ed836 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 24 Jun 2011 01:34:47 -0400 Subject: pep8 fixes --- nova/api/openstack/contrib/flavorextraspecs.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py index 7fc8e7954..8518bf2bd 100644 --- a/nova/api/openstack/contrib/flavorextraspecs.py +++ b/nova/api/openstack/contrib/flavorextraspecs.py @@ -122,6 +122,5 @@ class Flavorextraspecs(extensions.ExtensionDescriptor): FlavorExtraSpecsController(), parent=dict(member_name='flavor', collection_name='flavors')) - resources.append(res) return resources -- cgit From 4c46c44d7c1458ccaa3919110de12dfceef406c1 Mon Sep 17 00:00:00 2001 From: Lorin Hochstein Date: Fri, 24 Jun 2011 10:37:43 -0400 Subject: Removed an import --- nova/api/openstack/__init__.py | 1 - 1 file changed, 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 859cac669..f24017df0 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -32,7 +32,6 @@ from nova.api.openstack import faults from nova.api.openstack import backup_schedules from nova.api.openstack import consoles from nova.api.openstack import flavors -from nova.api.openstack import flavor_extra_specs from nova.api.openstack import images from nova.api.openstack import image_metadata from nova.api.openstack import ips -- cgit From c5745c0cb61bb6ab375a1e52d5e203a7a0a76366 Mon Sep 17 00:00:00 2001 From: Kirill Shileev Date: Fri, 24 Jun 2011 20:59:32 +0400 Subject: associate diassociate untested, first attept to test --- nova/api/openstack/contrib/floating_ips.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index dc048869c..23864c352 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -43,7 +43,7 @@ def _translate_floating_ips_view(floating_ips): class FloatingIPController(object): - """The Volumes API controller for the OpenStack API.""" + """The Floating IPs API controller for the OpenStack API.""" _serialization_metadata = { 'application/xml': { @@ -98,15 +98,32 @@ class FloatingIPController(object): return {'released': ip} - def associate(self, req, id, body): + def associate(self, req, id_ip, body): + """ /floating_ips/ip or id/associate fixed ip in body """ context = req.environ['nova.context'] + floating_ip = self._get_ip_by_id(context, id_ip) - return {'associate': None} + fixed_ip = body['associate_address']['fixed_ip'] - def disassociate(self, req, id, body): + try: + self.network_api.associate_floating_ip(context, floating_ip, fixed_ip) + except rpc.RemoteError: + raise + + return {'associated': [floating_ip, fixed_ip]} + + def disassociate(self, req, id_ip, body): + """ POST /floating_ips/{ip | ip_id}/disassociate """ context = req.environ['nova.context'] - return {'disassociate': None} + floating_ip = self._get_ip_by_id(context, id_ip) + + try: + self.network_api.disassociate_floating_ip(context, floating_ip) + except rpc.RemoteError: + raise + + return {'disassociated': floating_ip} def _get_ip_by_id(self, context, value): """Checks that value is id and then returns its address. -- cgit From 09d439cd74290a6b2532376afc94d2c8e23cdda6 Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Fri, 24 Jun 2011 23:55:18 +0400 Subject: stub tests --- nova/api/openstack/contrib/floating_ips.py | 1 + 1 file changed, 1 insertion(+) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 23864c352..924b504a8 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -2,6 +2,7 @@ # Copyright 2011 OpenStack LLC. # Copyright 2011 Grid Dynamics +# Copyright 2011 Eldar Nugaev, Kirill Shileev, Ilya Alekseyev # # 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 -- cgit From a770864d308242bfcfa8dadb210595785d8fa71f Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Sat, 25 Jun 2011 02:42:27 +0400 Subject: tests --- nova/api/openstack/contrib/floating_ips.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index c6bc85c61..95502a5c5 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -104,10 +104,10 @@ class FloatingIPController(object): "id": ip['id'], "floating_ip": ip['address']}} - def associate(self, req, id_ip, body): + def associate(self, req, id, body): """ /floating_ips/{id}/associate fixed ip in body """ context = req.environ['nova.context'] - floating_ip = self._get_ip_by_id(context, id_ip) + floating_ip = self._get_ip_by_id(context, id) fixed_ip = body['associate_address']['fixed_ip'] @@ -117,13 +117,17 @@ class FloatingIPController(object): except rpc.RemoteError: raise - return {'associated': [floating_ip, fixed_ip]} + return {'associated': + { + "floating_ip_id": id, + "floating_ip": floating_ip, + "fixed_ip": fixed_ip}} - def disassociate(self, req, id_ip, body): + def disassociate(self, req, id, body): """ POST /floating_ips/{id}/disassociate """ context = req.environ['nova.context'] - floating_ip = self._get_ip_by_id(context, id_ip) + floating_ip = self._get_ip_by_id(context, id) try: self.network_api.disassociate_floating_ip(context, floating_ip) -- cgit From 5e4d90b33ddb993294232eea168a768486ba0bf4 Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Sat, 25 Jun 2011 03:05:09 +0400 Subject: added disassociate method to tests --- nova/api/openstack/contrib/floating_ips.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 95502a5c5..467b46712 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -91,7 +91,7 @@ class FloatingIPController(object): raise return {'allocated': { - "id" : ip['id'], + "id": ip['id'], "floating_ip": ip['address']}} def delete(self, req, id): @@ -126,15 +126,17 @@ class FloatingIPController(object): def disassociate(self, req, id, body): """ POST /floating_ips/{id}/disassociate """ context = req.environ['nova.context'] - - floating_ip = self._get_ip_by_id(context, id) + floating_ip = self.network_api.get(context, id) + address = floating_ip['address'] + fixed_ip = floating_ip['fixed_ip']['address'] try: - self.network_api.disassociate_floating_ip(context, floating_ip) + self.network_api.disassociate_floating_ip(context, address) except rpc.RemoteError: raise - return {'disassociated': floating_ip} + return {'disassociated': {'floating_ip': address, + 'fixed_ip': fixed_ip}} def _get_ip_by_id(self, context, value): """Checks that value is id and then returns its address.""" -- cgit From ef1f4d33fa5763ea602c2fc1098a4b230b86e82b Mon Sep 17 00:00:00 2001 From: Ilya Alekseyev Date: Mon, 27 Jun 2011 16:33:01 +0400 Subject: review issues fixed --- nova/api/openstack/contrib/floating_ips.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index 467b46712..ca192f501 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -65,7 +65,7 @@ class FloatingIPController(object): context = req.environ['nova.context'] try: - floating_ip = self.network_api.get(context, id) + floating_ip = self.network_api.get_floating_ip(context, id) except exception.NotFound: return faults.Fault(exc.HTTPNotFound()) @@ -74,7 +74,7 @@ class FloatingIPController(object): def index(self, req): context = req.environ['nova.context'] - floating_ips = self.network_api.list(context) + floating_ips = self.network_api.list_floating_ips(context) return _translate_floating_ips_view(floating_ips) @@ -97,7 +97,7 @@ class FloatingIPController(object): def delete(self, req, id): context = req.environ['nova.context'] - ip = self.network_api.get(context, id) + ip = self.network_api.get_floating_ip(context, id) self.network_api.release_floating_ip(context, address=ip) return {'released': { @@ -126,7 +126,7 @@ class FloatingIPController(object): def disassociate(self, req, id, body): """ POST /floating_ips/{id}/disassociate """ context = req.environ['nova.context'] - floating_ip = self.network_api.get(context, id) + floating_ip = self.network_api.get_floating_ip(context, id) address = floating_ip['address'] fixed_ip = floating_ip['fixed_ip']['address'] @@ -140,7 +140,7 @@ class FloatingIPController(object): def _get_ip_by_id(self, context, value): """Checks that value is id and then returns its address.""" - return self.network_api.get(context, value)['address'] + return self.network_api.get_floating_ip(context, value)['address'] class Floating_ips(extensions.ExtensionDescriptor): @@ -148,7 +148,7 @@ class Floating_ips(extensions.ExtensionDescriptor): return "Floating_ips" def get_alias(self): - return "FLOATING_IPS" + return "os-floating-ips" def get_description(self): return "Floating IPs support" @@ -163,10 +163,10 @@ class Floating_ips(extensions.ExtensionDescriptor): resources = [] res = extensions.ResourceExtension('floating_ips', - FloatingIPController(), - member_actions={ - 'associate': 'POST', - 'disassociate': 'POST'}) + FloatingIPController(), + member_actions={ + 'associate': 'POST', + 'disassociate': 'POST'}) resources.append(res) return resources -- cgit From 7c4f83bc8f119ff4486f913bd3e5ef7eff5b338f Mon Sep 17 00:00:00 2001 From: Eldar Nugaev Date: Mon, 27 Jun 2011 20:36:53 +0400 Subject: changed extension alias to os-floating-ips --- nova/api/openstack/contrib/floating_ips.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'nova/api') diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py index ca192f501..914ec5bfb 100644 --- a/nova/api/openstack/contrib/floating_ips.py +++ b/nova/api/openstack/contrib/floating_ips.py @@ -162,7 +162,7 @@ class Floating_ips(extensions.ExtensionDescriptor): def get_resources(self): resources = [] - res = extensions.ResourceExtension('floating_ips', + res = extensions.ResourceExtension('os-floating-ips', FloatingIPController(), member_actions={ 'associate': 'POST', -- cgit