summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-08-22 08:28:12 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-08-22 08:28:12 -0400
commitba218353bcc905fd40ca4838c625fdbc371b9974 (patch)
tree082a088131f46394242ef74b6fd86bf8ec299bcf /nova/api
parent4d9cd63c2f269f795e476869557f6bd3d7dcc777 (diff)
parent0af1508c38bcf027153dd91c0d862307e90a164e (diff)
downloadnova-ba218353bcc905fd40ca4838c625fdbc371b9974.tar.gz
nova-ba218353bcc905fd40ca4838c625fdbc371b9974.tar.xz
nova-ba218353bcc905fd40ca4838c625fdbc371b9974.zip
merge with trunk
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/contrib/floating_ips.py114
-rw-r--r--nova/api/openstack/contrib/security_groups.py113
-rw-r--r--nova/api/openstack/create_instance_helper.py36
-rw-r--r--nova/api/openstack/schemas/v1.1/server.rng50
-rw-r--r--nova/api/openstack/schemas/v1.1/servers.rng6
-rw-r--r--nova/api/openstack/schemas/v1.1/servers_index.rng12
-rw-r--r--nova/api/openstack/servers.py14
-rw-r--r--nova/api/openstack/views/servers.py4
8 files changed, 281 insertions, 68 deletions
diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py
index 44b35c385..40086f778 100644
--- a/nova/api/openstack/contrib/floating_ips.py
+++ b/nova/api/openstack/contrib/floating_ips.py
@@ -15,8 +15,9 @@
# 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
+import webob
+from nova import compute
from nova import exception
from nova import log as logging
from nova import network
@@ -71,18 +72,22 @@ class FloatingIPController(object):
try:
floating_ip = self.network_api.get_floating_ip(context, id)
except exception.NotFound:
- return faults.Fault(exc.HTTPNotFound())
+ return faults.Fault(webob.exc.HTTPNotFound())
return _translate_floating_ip_view(floating_ip)
def index(self, req):
context = req.environ['nova.context']
- floating_ips = self.network_api.list_floating_ips(context)
+ try:
+ # FIXME(ja) - why does self.network_api.list_floating_ips raise?
+ floating_ips = self.network_api.list_floating_ips(context)
+ except exception.FloatingIpNotFoundForProject:
+ floating_ips = []
return _translate_floating_ips_view(floating_ips)
- def create(self, req):
+ def create(self, req, body=None):
context = req.environ['nova.context']
try:
@@ -95,63 +100,67 @@ class FloatingIPController(object):
else:
raise
- return {'allocated': {
- "id": ip['id'],
- "floating_ip": ip['address']}}
+ return _translate_floating_ip_view(ip)
def delete(self, req, id):
context = req.environ['nova.context']
- ip = self.network_api.get_floating_ip(context, id)
+ floating_ip = self.network_api.get_floating_ip(context, id)
- if 'fixed_ip' in ip:
- self.disassociate(req, id)
+ if 'fixed_ip' in floating_ip:
+ self.network_api.disassociate_floating_ip(context,
+ floating_ip['address'])
- self.network_api.release_floating_ip(context, address=ip['address'])
+ self.network_api.release_floating_ip(context,
+ address=floating_ip['address'])
+ return webob.exc.HTTPAccepted()
- return {'released': {
- "id": ip['id'],
- "floating_ip": ip['address']}}
+ def _get_ip_by_id(self, context, value):
+ """Checks that value is id and then returns its address."""
+ return self.network_api.get_floating_ip(context, value)['address']
- 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)
- fixed_ip = body['associate_address']['fixed_ip']
+class Floating_ips(extensions.ExtensionDescriptor):
+ def __init__(self):
+ self.compute_api = compute.API()
+ self.network_api = network.API()
+ super(Floating_ips, self).__init__()
- try:
- self.network_api.associate_floating_ip(context,
- floating_ip, fixed_ip)
- except rpc.RemoteError:
- raise
-
- return {'associated':
- {
- "floating_ip_id": id,
- "floating_ip": floating_ip,
- "fixed_ip": fixed_ip}}
-
- def disassociate(self, req, id, body=None):
- """ POST /floating_ips/{id}/disassociate """
+ def _add_floating_ip(self, input_dict, req, instance_id):
+ """Associate floating_ip to an instance."""
context = req.environ['nova.context']
- floating_ip = self.network_api.get_floating_ip(context, id)
- address = floating_ip['address']
- fixed_ip = floating_ip['fixed_ip']['address']
try:
- self.network_api.disassociate_floating_ip(context, address)
- except rpc.RemoteError:
- raise
+ address = input_dict['addFloatingIp']['address']
+ except TypeError:
+ msg = _("Missing parameter dict")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except KeyError:
+ msg = _("Address not specified")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
- return {'disassociated': {'floating_ip': address,
- 'fixed_ip': fixed_ip}}
+ self.compute_api.associate_floating_ip(context, instance_id, address)
- def _get_ip_by_id(self, context, value):
- """Checks that value is id and then returns its address."""
- return self.network_api.get_floating_ip(context, value)['address']
+ return webob.Response(status_int=202)
+ def _remove_floating_ip(self, input_dict, req, instance_id):
+ """Dissociate floating_ip from an instance."""
+ context = req.environ['nova.context']
+
+ try:
+ address = input_dict['removeFloatingIp']['address']
+ except TypeError:
+ msg = _("Missing parameter dict")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except KeyError:
+ msg = _("Address not specified")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ floating_ip = self.network_api.get_floating_ip_by_ip(context, address)
+ if 'fixed_ip' in floating_ip:
+ self.network_api.disassociate_floating_ip(context, address)
+
+ return webob.Response(status_int=202)
-class Floating_ips(extensions.ExtensionDescriptor):
def get_name(self):
return "Floating_ips"
@@ -172,9 +181,18 @@ class Floating_ips(extensions.ExtensionDescriptor):
res = extensions.ResourceExtension('os-floating-ips',
FloatingIPController(),
- member_actions={
- 'associate': 'POST',
- 'disassociate': 'POST'})
+ member_actions={})
resources.append(res)
return resources
+
+ def get_actions(self):
+ """Return the actions the extension adds, as required by contract."""
+ actions = [
+ extensions.ActionExtension("servers", "addFloatingIp",
+ self._add_floating_ip),
+ extensions.ActionExtension("servers", "removeFloatingIp",
+ self._remove_floating_ip),
+ ]
+
+ return actions
diff --git a/nova/api/openstack/contrib/security_groups.py b/nova/api/openstack/contrib/security_groups.py
index 6c57fbb51..1fd64f3b8 100644
--- a/nova/api/openstack/contrib/security_groups.py
+++ b/nova/api/openstack/contrib/security_groups.py
@@ -25,10 +25,11 @@ from nova import db
from nova import exception
from nova import flags
from nova import log as logging
+from nova import rpc
from nova.api.openstack import common
from nova.api.openstack import extensions
from nova.api.openstack import wsgi
-
+from nova.compute import power_state
from xml.dom import minidom
@@ -73,33 +74,28 @@ class SecurityGroupController(object):
context, rule)]
return security_group
- def show(self, req, id):
- """Return data about the given security group."""
- context = req.environ['nova.context']
+ def _get_security_group(self, context, id):
try:
id = int(id)
security_group = db.security_group_get(context, id)
except ValueError:
- msg = _("Security group id is not integer")
- return exc.HTTPBadRequest(explanation=msg)
+ msg = _("Security group id should be integer")
+ raise exc.HTTPBadRequest(explanation=msg)
except exception.NotFound as exp:
- return exc.HTTPNotFound(explanation=unicode(exp))
+ raise exc.HTTPNotFound(explanation=unicode(exp))
+ return security_group
+ def show(self, req, id):
+ """Return data about the given security group."""
+ context = req.environ['nova.context']
+ security_group = self._get_security_group(context, id)
return {'security_group': self._format_security_group(context,
security_group)}
def delete(self, req, id):
"""Delete a security group."""
context = req.environ['nova.context']
- try:
- id = int(id)
- security_group = db.security_group_get(context, id)
- except ValueError:
- msg = _("Security group id is not integer")
- return exc.HTTPBadRequest(explanation=msg)
- except exception.SecurityGroupNotFound as exp:
- return exc.HTTPNotFound(explanation=unicode(exp))
-
+ security_group = self._get_security_group(context, id)
LOG.audit(_("Delete security group %s"), id, context=context)
db.security_group_destroy(context, security_group.id)
@@ -226,9 +222,9 @@ class SecurityGroupRulesController(SecurityGroupController):
security_group_rule = db.security_group_rule_create(context, values)
self.compute_api.trigger_security_group_rules_refresh(context,
- security_group_id=security_group['id'])
+ security_group_id=security_group['id'])
- return {'security_group_rule': self._format_security_group_rule(
+ return {"security_group_rule": self._format_security_group_rule(
context,
security_group_rule)}
@@ -336,6 +332,11 @@ class SecurityGroupRulesController(SecurityGroupController):
class Security_groups(extensions.ExtensionDescriptor):
+
+ def __init__(self):
+ self.compute_api = compute.API()
+ super(Security_groups, self).__init__()
+
def get_name(self):
return "SecurityGroups"
@@ -351,6 +352,82 @@ class Security_groups(extensions.ExtensionDescriptor):
def get_updated(self):
return "2011-07-21T00:00:00+00:00"
+ def _addSecurityGroup(self, input_dict, req, instance_id):
+ context = req.environ['nova.context']
+
+ try:
+ body = input_dict['addSecurityGroup']
+ group_name = body['name']
+ instance_id = int(instance_id)
+ except ValueError:
+ msg = _("Server id should be integer")
+ raise exc.HTTPBadRequest(explanation=msg)
+ except TypeError:
+ msg = _("Missing parameter dict")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except KeyError:
+ msg = _("Security group not specified")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ if not group_name or group_name.strip() == '':
+ msg = _("Security group name cannot be empty")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ self.compute_api.add_security_group(context, instance_id,
+ group_name)
+ except exception.SecurityGroupNotFound as exp:
+ return exc.HTTPNotFound(explanation=unicode(exp))
+ except exception.InstanceNotFound as exp:
+ return exc.HTTPNotFound(explanation=unicode(exp))
+ except exception.Invalid as exp:
+ return exc.HTTPBadRequest(explanation=unicode(exp))
+
+ return exc.HTTPAccepted()
+
+ def _removeSecurityGroup(self, input_dict, req, instance_id):
+ context = req.environ['nova.context']
+
+ try:
+ body = input_dict['removeSecurityGroup']
+ group_name = body['name']
+ instance_id = int(instance_id)
+ except ValueError:
+ msg = _("Server id should be integer")
+ raise exc.HTTPBadRequest(explanation=msg)
+ except TypeError:
+ msg = _("Missing parameter dict")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+ except KeyError:
+ msg = _("Security group not specified")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ if not group_name or group_name.strip() == '':
+ msg = _("Security group name cannot be empty")
+ raise webob.exc.HTTPBadRequest(explanation=msg)
+
+ try:
+ self.compute_api.remove_security_group(context, instance_id,
+ group_name)
+ except exception.SecurityGroupNotFound as exp:
+ return exc.HTTPNotFound(explanation=unicode(exp))
+ except exception.InstanceNotFound as exp:
+ return exc.HTTPNotFound(explanation=unicode(exp))
+ except exception.Invalid as exp:
+ return exc.HTTPBadRequest(explanation=unicode(exp))
+
+ return exc.HTTPAccepted()
+
+ def get_actions(self):
+ """Return the actions the extensions adds"""
+ actions = [
+ extensions.ActionExtension("servers", "addSecurityGroup",
+ self._addSecurityGroup),
+ extensions.ActionExtension("servers", "removeSecurityGroup",
+ self._removeSecurityGroup)
+ ]
+ return actions
+
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/create_instance_helper.py b/nova/api/openstack/create_instance_helper.py
index 978741682..339f260b9 100644
--- a/nova/api/openstack/create_instance_helper.py
+++ b/nova/api/openstack/create_instance_helper.py
@@ -111,6 +111,15 @@ class CreateInstanceHelper(object):
if personality:
injected_files = self._get_injected_files(personality)
+ sg_names = []
+ security_groups = server_dict.get('security_groups')
+ if security_groups is not None:
+ sg_names = [sg['name'] for sg in security_groups if sg.get('name')]
+ if not sg_names:
+ sg_names.append('default')
+
+ sg_names = list(set(sg_names))
+
try:
flavor_id = self.controller._flavor_id_from_req_data(body)
except ValueError as error:
@@ -158,12 +167,15 @@ class CreateInstanceHelper(object):
key_name=key_name,
key_data=key_data,
metadata=server_dict.get('metadata', {}),
+ access_ip_v4=server_dict.get('accessIPv4'),
+ access_ip_v6=server_dict.get('accessIPv6'),
injected_files=injected_files,
admin_password=password,
zone_blob=zone_blob,
reservation_id=reservation_id,
min_count=min_count,
max_count=max_count,
+ security_group=sg_names,
user_data=user_data,
availability_zone=availability_zone))
except quota.QuotaError as error:
@@ -174,6 +186,8 @@ class CreateInstanceHelper(object):
except exception.FlavorNotFound as error:
msg = _("Invalid flavorRef provided.")
raise exc.HTTPBadRequest(explanation=msg)
+ except exception.SecurityGroupNotFound as error:
+ raise exc.HTTPBadRequest(explanation=unicode(error))
# Let the caller deal with unhandled exceptions.
def _handle_quota_error(self, error):
@@ -452,7 +466,8 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer):
server = {}
server_node = self.find_first_child_named(node, 'server')
- attributes = ["name", "imageRef", "flavorRef", "adminPass"]
+ attributes = ["name", "imageRef", "flavorRef", "adminPass",
+ "accessIPv4", "accessIPv6"]
for attr in attributes:
if server_node.getAttribute(attr):
server[attr] = server_node.getAttribute(attr)
@@ -465,6 +480,10 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer):
if personality is not None:
server["personality"] = personality
+ security_groups = self._extract_security_groups(server_node)
+ if security_groups is not None:
+ server["security_groups"] = security_groups
+
return server
def _extract_personality(self, server_node):
@@ -481,3 +500,18 @@ class ServerXMLDeserializerV11(wsgi.MetadataXMLDeserializer):
return personality
else:
return None
+
+ def _extract_security_groups(self, server_node):
+ """Marshal the security_groups attribute of a parsed request"""
+ node = self.find_first_child_named(server_node, "security_groups")
+ if node is not None:
+ security_groups = []
+ for sg_node in self.find_children_named(node, "security_group"):
+ item = {}
+ name_node = self.find_first_child_named(sg_node, "name")
+ if name_node:
+ item["name"] = self.extract_text(name_node)
+ security_groups.append(item)
+ return security_groups
+ else:
+ return None
diff --git a/nova/api/openstack/schemas/v1.1/server.rng b/nova/api/openstack/schemas/v1.1/server.rng
new file mode 100644
index 000000000..dbd169a83
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/server.rng
@@ -0,0 +1,50 @@
+<element name="server" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <attribute name="uuid"> <text/> </attribute>
+ <attribute name="updated"> <text/> </attribute>
+ <attribute name="created"> <text/> </attribute>
+ <attribute name="hostId"> <text/> </attribute>
+ <attribute name="accessIPv4"> <text/> </attribute>
+ <attribute name="accessIPv6"> <text/> </attribute>
+ <attribute name="status"> <text/> </attribute>
+ <optional>
+ <attribute name="progress"> <text/> </attribute>
+ </optional>
+ <optional>
+ <attribute name="adminPass"> <text/> </attribute>
+ </optional>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+ <element name="image">
+ <attribute name="id"> <text/> </attribute>
+ <externalRef href="../atom-link.rng"/>
+ </element>
+ <element name="flavor">
+ <attribute name="id"> <text/> </attribute>
+ <externalRef href="../atom-link.rng"/>
+ </element>
+ <element name="metadata">
+ <zeroOrMore>
+ <element name="meta">
+ <attribute name="key"> <text/> </attribute>
+ <text/>
+ </element>
+ </zeroOrMore>
+ </element>
+ <element name="addresses">
+ <zeroOrMore>
+ <element name="network">
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <element name="ip">
+ <attribute name="version"> <text/> </attribute>
+ <attribute name="addr"> <text/> </attribute>
+ </element>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+ </element>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/servers.rng b/nova/api/openstack/schemas/v1.1/servers.rng
new file mode 100644
index 000000000..4e2bb8853
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/servers.rng
@@ -0,0 +1,6 @@
+<element name="servers" xmlns="http://relaxng.org/ns/structure/1.0"
+ ns="http://docs.openstack.org/compute/api/v1.1">
+ <zeroOrMore>
+ <externalRef href="server.rng"/>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/schemas/v1.1/servers_index.rng b/nova/api/openstack/schemas/v1.1/servers_index.rng
new file mode 100644
index 000000000..768f0912d
--- /dev/null
+++ b/nova/api/openstack/schemas/v1.1/servers_index.rng
@@ -0,0 +1,12 @@
+<element name="servers" ns="http://docs.openstack.org/compute/api/v1.1"
+ xmlns="http://relaxng.org/ns/structure/1.0">
+ <zeroOrMore>
+ <element name="server">
+ <attribute name="name"> <text/> </attribute>
+ <attribute name="id"> <text/> </attribute>
+ <zeroOrMore>
+ <externalRef href="../atom-link.rng"/>
+ </zeroOrMore>
+ </element>
+ </zeroOrMore>
+</element>
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 214174273..553357404 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -163,7 +163,7 @@ class Controller(object):
@scheduler_api.redirect_handler
def update(self, req, id, body):
- """Update server name then pass on to version-specific controller"""
+ """Update server then pass on to version-specific controller"""
if len(req.body) == 0:
raise exc.HTTPUnprocessableEntity()
@@ -178,6 +178,14 @@ class Controller(object):
self.helper._validate_server_name(name)
update_dict['display_name'] = name.strip()
+ if 'accessIPv4' in body['server']:
+ access_ipv4 = body['server']['accessIPv4']
+ update_dict['access_ip_v4'] = access_ipv4.strip()
+
+ if 'accessIPv6' in body['server']:
+ access_ipv6 = body['server']['accessIPv6']
+ update_dict['access_ip_v6'] = access_ipv6.strip()
+
try:
self.compute_api.update(ctxt, id, **update_dict)
except exception.NotFound:
@@ -839,6 +847,10 @@ class ServerXMLSerializer(wsgi.XMLDictSerializer):
node.setAttribute('created', str(server['created']))
node.setAttribute('updated', str(server['updated']))
node.setAttribute('status', server['status'])
+ if 'accessIPv4' in server:
+ node.setAttribute('accessIPv4', str(server['accessIPv4']))
+ if 'accessIPv6' in server:
+ node.setAttribute('accessIPv6', str(server['accessIPv6']))
if 'progress' in server:
node.setAttribute('progress', str(server['progress']))
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 5835949f1..465287adc 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -144,6 +144,10 @@ class ViewBuilderV11(ViewBuilder):
response['server']['progress'] = 100
elif response['server']['status'] == "BUILD":
response['server']['progress'] = 0
+
+ response['server']['accessIPv4'] = inst.get('access_ip_v4') or ""
+ response['server']['accessIPv6'] = inst.get('access_ip_v6') or ""
+
return response
def _build_image(self, response, inst):