merge with trunk, resolve conflicts
This commit is contained in:
commit
01409c0553
|
@ -15,4 +15,5 @@ run_tests.log
|
|||
tests.sqlite
|
||||
nova/tests/instance-*
|
||||
tags
|
||||
|
||||
.coverage
|
||||
covhtml
|
||||
|
|
1
Authors
1
Authors
|
@ -8,6 +8,7 @@ Anne Gentle <anne@openstack.org>
|
|||
Anthony Young <sleepsonthefloor@gmail.com>
|
||||
Antony Messerli <ant@openstack.org>
|
||||
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
|
||||
Arvind Somya <asomya@cisco.com>
|
||||
Bilal Akhtar <bilalakhtar@ubuntu.com>
|
||||
Brian Lamar <brian.lamar@rackspace.com>
|
||||
Brian Schott <bschott@isi.edu>
|
||||
|
|
|
@ -530,7 +530,52 @@ class CloudController(object):
|
|||
g['ipPermissions'] += [r]
|
||||
return g
|
||||
|
||||
def _revoke_rule_args_to_dict(self, context, to_port=None, from_port=None,
|
||||
def _rule_args_to_dict(self, context, kwargs):
|
||||
rules = []
|
||||
if not 'groups' in kwargs and not 'ip_ranges' in kwargs:
|
||||
rule = self._rule_dict_last_step(context, **kwargs)
|
||||
if rule:
|
||||
rules.append(rule)
|
||||
return rules
|
||||
if 'ip_ranges' in kwargs:
|
||||
rules = self._cidr_args_split(kwargs)
|
||||
finalset = []
|
||||
for rule in rules:
|
||||
if 'groups' in rule:
|
||||
groups_values = self._groups_args_split(rule)
|
||||
for groups_value in groups_values:
|
||||
finalset.append(groups_value)
|
||||
else:
|
||||
if rule:
|
||||
finalset.append(rule)
|
||||
return finalset
|
||||
|
||||
def _cidr_args_split(self, kwargs):
|
||||
cidr_args_split = []
|
||||
cidrs = kwargs['ip_ranges']
|
||||
for key, cidr in cidrs.iteritems():
|
||||
mykwargs = kwargs.copy()
|
||||
del mykwargs['ip_ranges']
|
||||
mykwargs['cidr_ip'] = cidr['cidr_ip']
|
||||
cidr_args_split.append(mykwargs)
|
||||
return cidr_args_split
|
||||
|
||||
def _groups_args_split(self, kwargs):
|
||||
groups_args_split = []
|
||||
groups = kwargs['groups']
|
||||
for key, group in groups.iteritems():
|
||||
mykwargs = kwargs.copy()
|
||||
del mykwargs['groups']
|
||||
if 'group_name' in group:
|
||||
mykwargs['source_security_group_name'] = group['group_name']
|
||||
if 'user_id' in group:
|
||||
mykwargs['source_security_group_owner_id'] = group['user_id']
|
||||
if 'group_id' in group:
|
||||
mykwargs['source_security_group_id'] = group['group_id']
|
||||
groups_args_split.append(mykwargs)
|
||||
return groups_args_split
|
||||
|
||||
def _rule_dict_last_step(self, context, to_port=None, from_port=None,
|
||||
ip_protocol=None, cidr_ip=None, user_id=None,
|
||||
source_security_group_name=None,
|
||||
source_security_group_owner_id=None):
|
||||
|
@ -615,7 +660,7 @@ class CloudController(object):
|
|||
msg = "Revoke security group ingress %s"
|
||||
LOG.audit(_(msg), security_group['name'], context=context)
|
||||
|
||||
criteria = self._revoke_rule_args_to_dict(context, **kwargs)
|
||||
criteria = self._rule_args_to_dict(context, kwargs)[0]
|
||||
if criteria is None:
|
||||
raise exception.ApiError(_("Not enough parameters to build a "
|
||||
"valid rule."))
|
||||
|
@ -656,21 +701,34 @@ class CloudController(object):
|
|||
|
||||
msg = "Authorize security group ingress %s"
|
||||
LOG.audit(_(msg), security_group['name'], context=context)
|
||||
values = self._revoke_rule_args_to_dict(context, **kwargs)
|
||||
if values is None:
|
||||
raise exception.ApiError(_("Not enough parameters to build a "
|
||||
"valid rule."))
|
||||
values['parent_group_id'] = security_group.id
|
||||
prevalues = []
|
||||
try:
|
||||
prevalues = kwargs['ip_permissions']
|
||||
except KeyError:
|
||||
prevalues.append(kwargs)
|
||||
postvalues = []
|
||||
for values in prevalues:
|
||||
rulesvalues = self._rule_args_to_dict(context, values)
|
||||
if not rulesvalues:
|
||||
err = "%s Not enough parameters to build a valid rule"
|
||||
raise exception.ApiError(_(err % rulesvalues))
|
||||
for values_for_rule in rulesvalues:
|
||||
values_for_rule['parent_group_id'] = security_group.id
|
||||
if self._security_group_rule_exists(security_group,
|
||||
values_for_rule):
|
||||
err = '%s - This rule already exists in group'
|
||||
raise exception.ApiError(_(err) % values_for_rule)
|
||||
postvalues.append(values_for_rule)
|
||||
|
||||
if self._security_group_rule_exists(security_group, values):
|
||||
raise exception.ApiError(_('This rule already exists in group %s')
|
||||
% group_name)
|
||||
|
||||
security_group_rule = db.security_group_rule_create(context, values)
|
||||
for values_for_rule in postvalues:
|
||||
security_group_rule = db.security_group_rule_create(context,
|
||||
values_for_rule)
|
||||
|
||||
self.compute_api.trigger_security_group_rules_refresh(context,
|
||||
security_group_id=security_group['id'])
|
||||
security_group_id=security_group['id'])
|
||||
|
||||
group = db.security_group_get_by_name(context, context.project_id,
|
||||
security_group['name'])
|
||||
return True
|
||||
|
||||
def _get_source_project_id(self, context, source_security_group_owner_id):
|
||||
|
@ -1147,7 +1205,7 @@ class CloudController(object):
|
|||
|
||||
def rescue_instance(self, context, instance_id, **kwargs):
|
||||
"""This is an extension to the normal ec2_api"""
|
||||
self._do_instance(self.compute_api.rescue, contect, instnace_id)
|
||||
self._do_instance(self.compute_api.rescue, context, instance_id)
|
||||
return True
|
||||
|
||||
def unrescue_instance(self, context, instance_id, **kwargs):
|
||||
|
|
|
@ -164,11 +164,17 @@ class APIRouterV11(APIRouter):
|
|||
|
||||
def _setup_routes(self, mapper):
|
||||
super(APIRouterV11, self)._setup_routes(mapper, '1.1')
|
||||
mapper.resource("image_meta", "meta",
|
||||
controller=image_metadata.create_resource(),
|
||||
image_metadata_controller = image_metadata.create_resource()
|
||||
mapper.resource("image_meta", "metadata",
|
||||
controller=image_metadata_controller,
|
||||
parent_resource=dict(member_name='image',
|
||||
collection_name='images'))
|
||||
|
||||
mapper.connect("metadata", "/images/{image_id}/metadata",
|
||||
controller=image_metadata_controller,
|
||||
action='update_all',
|
||||
conditions={"method": ['PUT']})
|
||||
|
||||
mapper.resource("server_meta", "meta",
|
||||
controller=server_metadata.create_resource(),
|
||||
parent_resource=dict(member_name='server',
|
||||
|
|
|
@ -167,3 +167,28 @@ def remove_version_from_href(href):
|
|||
msg = _('href does not contain version')
|
||||
raise ValueError(msg)
|
||||
return new_href
|
||||
|
||||
|
||||
def get_version_from_href(href):
|
||||
"""Returns the api version in the href.
|
||||
|
||||
Returns the api version in the href.
|
||||
If no version is found, 1.0 is returned
|
||||
|
||||
Given: 'http://www.nova.com/123'
|
||||
Returns: '1.0'
|
||||
|
||||
Given: 'http://www.nova.com/v1.1'
|
||||
Returns: '1.1'
|
||||
|
||||
"""
|
||||
try:
|
||||
#finds the first instance that matches /v#.#/
|
||||
version = re.findall(r'[/][v][0-9]+\.[0-9]+[/]', href)
|
||||
#if no version was found, try finding /v#.# at the end of the string
|
||||
if not version:
|
||||
version = re.findall(r'[/][v][0-9]+\.[0-9]+$', href)
|
||||
version = re.findall(r'[0-9]+\.[0-9]', version[0])[0]
|
||||
except IndexError:
|
||||
version = '1.0'
|
||||
return version
|
||||
|
|
|
@ -71,9 +71,12 @@ class CreateInstanceHelper(object):
|
|||
if not body:
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
|
||||
context = req.environ['nova.context']
|
||||
if not 'server' in body:
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
|
||||
password = self.controller._get_server_admin_password(body['server'])
|
||||
server_dict = body['server']
|
||||
context = req.environ['nova.context']
|
||||
password = self.controller._get_server_admin_password(server_dict)
|
||||
|
||||
key_name = None
|
||||
key_data = None
|
||||
|
@ -95,7 +98,7 @@ class CreateInstanceHelper(object):
|
|||
locals())
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
personality = body['server'].get('personality')
|
||||
personality = server_dict.get('personality')
|
||||
|
||||
injected_files = []
|
||||
if personality:
|
||||
|
@ -107,18 +110,18 @@ class CreateInstanceHelper(object):
|
|||
msg = _("Invalid flavorRef provided.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if not 'name' in body['server']:
|
||||
if not 'name' in server_dict:
|
||||
msg = _("Server name is not defined")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
zone_blob = body['server'].get('blob')
|
||||
name = body['server']['name']
|
||||
zone_blob = server_dict.get('blob')
|
||||
name = server_dict['name']
|
||||
self._validate_server_name(name)
|
||||
name = name.strip()
|
||||
|
||||
reservation_id = body['server'].get('reservation_id')
|
||||
min_count = body['server'].get('min_count')
|
||||
max_count = body['server'].get('max_count')
|
||||
reservation_id = server_dict.get('reservation_id')
|
||||
min_count = server_dict.get('min_count')
|
||||
max_count = server_dict.get('max_count')
|
||||
# min_count and max_count are optional. If they exist, they come
|
||||
# in as strings. We want to default 'min_count' to 1, and default
|
||||
# 'max_count' to be 'min_count'.
|
||||
|
@ -145,7 +148,7 @@ class CreateInstanceHelper(object):
|
|||
display_description=name,
|
||||
key_name=key_name,
|
||||
key_data=key_data,
|
||||
metadata=body['server'].get('metadata', {}),
|
||||
metadata=server_dict.get('metadata', {}),
|
||||
injected_files=injected_files,
|
||||
admin_password=password,
|
||||
zone_blob=zone_blob,
|
||||
|
@ -282,7 +285,7 @@ class CreateInstanceHelper(object):
|
|||
return password
|
||||
|
||||
|
||||
class ServerXMLDeserializer(wsgi.XMLDeserializer):
|
||||
class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
"""
|
||||
Deserializer to handle xml-formatted server create requests.
|
||||
|
||||
|
@ -299,11 +302,12 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
|
|||
def _extract_server(self, node):
|
||||
"""Marshal the server attribute of a parsed request"""
|
||||
server = {}
|
||||
server_node = self._find_first_child_named(node, 'server')
|
||||
server_node = self.find_first_child_named(node, 'server')
|
||||
for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]:
|
||||
if server_node.getAttribute(attr):
|
||||
server[attr] = server_node.getAttribute(attr)
|
||||
metadata = self._extract_metadata(server_node)
|
||||
metadata_node = self.find_first_child_named(server_node, "metadata")
|
||||
metadata = self.extract_metadata(metadata_node)
|
||||
if metadata is not None:
|
||||
server["metadata"] = metadata
|
||||
personality = self._extract_personality(server_node)
|
||||
|
@ -311,49 +315,17 @@ class ServerXMLDeserializer(wsgi.XMLDeserializer):
|
|||
server["personality"] = personality
|
||||
return server
|
||||
|
||||
def _extract_metadata(self, server_node):
|
||||
"""Marshal the metadata attribute of a parsed request"""
|
||||
metadata_node = self._find_first_child_named(server_node, "metadata")
|
||||
if metadata_node is None:
|
||||
return None
|
||||
metadata = {}
|
||||
for meta_node in self._find_children_named(metadata_node, "meta"):
|
||||
key = meta_node.getAttribute("key")
|
||||
metadata[key] = self._extract_text(meta_node)
|
||||
return metadata
|
||||
|
||||
def _extract_personality(self, server_node):
|
||||
"""Marshal the personality attribute of a parsed request"""
|
||||
personality_node = \
|
||||
self._find_first_child_named(server_node, "personality")
|
||||
self.find_first_child_named(server_node, "personality")
|
||||
if personality_node is None:
|
||||
return None
|
||||
personality = []
|
||||
for file_node in self._find_children_named(personality_node, "file"):
|
||||
for file_node in self.find_children_named(personality_node, "file"):
|
||||
item = {}
|
||||
if file_node.hasAttribute("path"):
|
||||
item["path"] = file_node.getAttribute("path")
|
||||
item["contents"] = self._extract_text(file_node)
|
||||
item["contents"] = self.extract_text(file_node)
|
||||
personality.append(item)
|
||||
return personality
|
||||
|
||||
def _find_first_child_named(self, parent, name):
|
||||
"""Search a nodes children for the first child with a given name"""
|
||||
for node in parent.childNodes:
|
||||
if node.nodeName == name:
|
||||
return node
|
||||
return None
|
||||
|
||||
def _find_children_named(self, parent, name):
|
||||
"""Return all of a nodes children who have the given name"""
|
||||
for node in parent.childNodes:
|
||||
if node.nodeName == name:
|
||||
yield node
|
||||
|
||||
def _extract_text(self, node):
|
||||
"""Get the text field contained by the given node"""
|
||||
if len(node.childNodes) == 1:
|
||||
child = node.childNodes[0]
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
return child.nodeValue
|
||||
return ""
|
||||
|
|
|
@ -23,6 +23,7 @@ import sys
|
|||
import routes
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
|
@ -194,7 +195,7 @@ class ExtensionsResource(wsgi.Resource):
|
|||
def show(self, req, id):
|
||||
# NOTE(dprince): the extensions alias is used as the 'id' for show
|
||||
ext = self.extension_manager.extensions[id]
|
||||
return self._translate(ext)
|
||||
return dict(extension=self._translate(ext))
|
||||
|
||||
def delete(self, req, id):
|
||||
raise faults.Fault(webob.exc.HTTPNotFound())
|
||||
|
@ -258,15 +259,18 @@ class ExtensionMiddleware(base_wsgi.Middleware):
|
|||
|
||||
mapper = routes.Mapper()
|
||||
|
||||
serializer = wsgi.ResponseSerializer(
|
||||
{'application/xml': ExtensionsXMLSerializer()})
|
||||
# extended resources
|
||||
for resource in ext_mgr.get_resources():
|
||||
LOG.debug(_('Extended resource: %s'),
|
||||
resource.collection)
|
||||
mapper.resource(resource.collection, resource.collection,
|
||||
controller=wsgi.Resource(resource.controller),
|
||||
collection=resource.collection_actions,
|
||||
member=resource.member_actions,
|
||||
parent_resource=resource.parent)
|
||||
controller=wsgi.Resource(
|
||||
resource.controller, serializer=serializer),
|
||||
collection=resource.collection_actions,
|
||||
member=resource.member_actions,
|
||||
parent_resource=resource.parent)
|
||||
|
||||
# extended actions
|
||||
action_resources = self._action_ext_resources(application, ext_mgr,
|
||||
|
@ -462,3 +466,40 @@ class ResourceExtension(object):
|
|||
self.parent = parent
|
||||
self.collection_actions = collection_actions
|
||||
self.member_actions = member_actions
|
||||
|
||||
|
||||
class ExtensionsXMLSerializer(wsgi.XMLDictSerializer):
|
||||
|
||||
def show(self, ext_dict):
|
||||
ext = self._create_ext_elem(ext_dict['extension'])
|
||||
return self._to_xml(ext)
|
||||
|
||||
def index(self, exts_dict):
|
||||
exts = ElementTree.Element('extensions')
|
||||
for ext_dict in exts_dict['extensions']:
|
||||
exts.append(self._create_ext_elem(ext_dict))
|
||||
return self._to_xml(exts)
|
||||
|
||||
def _create_ext_elem(self, ext_dict):
|
||||
"""Create an extension xml element from a dict."""
|
||||
ext_elem = ElementTree.Element('extension')
|
||||
ext_elem.set('name', ext_dict['name'])
|
||||
ext_elem.set('namespace', ext_dict['namespace'])
|
||||
ext_elem.set('alias', ext_dict['alias'])
|
||||
ext_elem.set('updated', ext_dict['updated'])
|
||||
desc = ElementTree.Element('description')
|
||||
desc.text = ext_dict['description']
|
||||
ext_elem.append(desc)
|
||||
for link in ext_dict.get('links', []):
|
||||
elem = ElementTree.Element('atom:link')
|
||||
elem.set('rel', link['rel'])
|
||||
elem.set('href', link['href'])
|
||||
elem.set('type', link['type'])
|
||||
ext_elem.append(elem)
|
||||
return ext_elem
|
||||
|
||||
def _to_xml(self, root):
|
||||
"""Convert the xml tree object to an xml string."""
|
||||
root.set('xmlns', wsgi.XMLNS_V11)
|
||||
root.set('xmlns:atom', wsgi.XMLNS_ATOM)
|
||||
return ElementTree.tostring(root, encoding='UTF-8')
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
|
@ -61,9 +62,13 @@ class Fault(webob.exc.HTTPException):
|
|||
|
||||
content_type = req.best_match_content_type()
|
||||
|
||||
xml_serializer = {
|
||||
'1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
|
||||
'1.1': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V11),
|
||||
}[common.get_version_from_href(req.url)]
|
||||
|
||||
serializer = {
|
||||
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=wsgi.XMLNS_V10),
|
||||
'application/xml': xml_serializer,
|
||||
'application/json': wsgi.JSONDictSerializer(),
|
||||
}[content_type]
|
||||
|
||||
|
@ -100,9 +105,13 @@ class OverLimitFault(webob.exc.HTTPException):
|
|||
content_type = request.best_match_content_type()
|
||||
metadata = {"attributes": {"overLimitFault": "code"}}
|
||||
|
||||
xml_serializer = {
|
||||
'1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
|
||||
'1.1': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V11),
|
||||
}[common.get_version_from_href(request.url)]
|
||||
|
||||
serializer = {
|
||||
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=wsgi.XMLNS_V10),
|
||||
'application/xml': xml_serializer,
|
||||
'application/json': wsgi.JSONDictSerializer(),
|
||||
}[content_type]
|
||||
|
||||
|
|
|
@ -96,8 +96,16 @@ class Controller(object):
|
|||
self._check_quota_limit(context, metadata)
|
||||
img['properties'] = metadata
|
||||
self.image_service.update(context, image_id, img, None)
|
||||
return dict(meta=meta)
|
||||
|
||||
return req.body
|
||||
def update_all(self, req, image_id, body):
|
||||
context = req.environ['nova.context']
|
||||
img = self.image_service.show(context, image_id)
|
||||
metadata = body.get('metadata', {})
|
||||
self._check_quota_limit(context, metadata)
|
||||
img['properties'] = metadata
|
||||
self.image_service.update(context, image_id, img, None)
|
||||
return dict(metadata=metadata)
|
||||
|
||||
def delete(self, req, image_id, id):
|
||||
context = req.environ['nova.context']
|
||||
|
@ -110,6 +118,32 @@ class Controller(object):
|
|||
self.image_service.update(context, image_id, img, None)
|
||||
|
||||
|
||||
class ImageMetadataXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
|
||||
def _extract_metadata_container(self, datastring):
|
||||
dom = minidom.parseString(datastring)
|
||||
metadata_node = self.find_first_child_named(dom, "metadata")
|
||||
metadata = self.extract_metadata(metadata_node)
|
||||
return {'body': {'metadata': metadata}}
|
||||
|
||||
def create(self, datastring):
|
||||
return self._extract_metadata_container(datastring)
|
||||
|
||||
def update_all(self, datastring):
|
||||
return self._extract_metadata_container(datastring)
|
||||
|
||||
def update(self, datastring):
|
||||
dom = minidom.parseString(datastring)
|
||||
metadata_item = self.extract_metadata(dom)
|
||||
return {'body': {'meta': metadata_item}}
|
||||
|
||||
|
||||
class HeadersSerializer(wsgi.ResponseHeadersSerializer):
|
||||
|
||||
def delete(self, response, data):
|
||||
response.status_int = 204
|
||||
|
||||
|
||||
class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
|
||||
def __init__(self, xmlns=wsgi.XMLNS_V11):
|
||||
super(ImageMetadataXMLSerializer, self).__init__(xmlns=xmlns)
|
||||
|
@ -143,6 +177,9 @@ class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
|
|||
def create(self, metadata_dict):
|
||||
return self._meta_list_to_xml_string(metadata_dict)
|
||||
|
||||
def update_all(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]
|
||||
|
@ -157,11 +194,21 @@ class ImageMetadataXMLSerializer(wsgi.XMLDictSerializer):
|
|||
def update(self, meta_item_dict):
|
||||
return self._meta_item_to_xml_string(meta_item_dict['meta'])
|
||||
|
||||
def default(self, *args, **kwargs):
|
||||
return ''
|
||||
|
||||
|
||||
def create_resource():
|
||||
headers_serializer = HeadersSerializer()
|
||||
|
||||
body_deserializers = {
|
||||
'application/xml': ImageMetadataXMLDeserializer(),
|
||||
}
|
||||
|
||||
body_serializers = {
|
||||
'application/xml': ImageMetadataXMLSerializer(),
|
||||
}
|
||||
serializer = wsgi.ResponseSerializer(body_serializers)
|
||||
serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer)
|
||||
deserializer = wsgi.RequestDeserializer(body_deserializers)
|
||||
|
||||
return wsgi.Resource(Controller(), serializer=serializer)
|
||||
return wsgi.Resource(Controller(), deserializer, serializer)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
# under the License.
|
||||
|
||||
import time
|
||||
from xml.dom import minidom
|
||||
|
||||
from webob import exc
|
||||
|
||||
|
@ -100,17 +101,51 @@ class ControllerV11(Controller):
|
|||
return nova.api.openstack.views.addresses.ViewBuilderV11()
|
||||
|
||||
|
||||
class IPXMLSerializer(wsgi.XMLDictSerializer):
|
||||
def __init__(self, xmlns=wsgi.XMLNS_V11):
|
||||
super(IPXMLSerializer, self).__init__(xmlns=xmlns)
|
||||
|
||||
def _ip_to_xml(self, xml_doc, ip_dict):
|
||||
ip_node = xml_doc.createElement('ip')
|
||||
ip_node.setAttribute('addr', ip_dict['addr'])
|
||||
ip_node.setAttribute('version', str(ip_dict['version']))
|
||||
return ip_node
|
||||
|
||||
def _network_to_xml(self, xml_doc, network_id, ip_dicts):
|
||||
network_node = xml_doc.createElement('network')
|
||||
network_node.setAttribute('id', network_id)
|
||||
|
||||
for ip_dict in ip_dicts:
|
||||
ip_node = self._ip_to_xml(xml_doc, ip_dict)
|
||||
network_node.appendChild(ip_node)
|
||||
|
||||
return network_node
|
||||
|
||||
def networks_to_xml(self, xml_doc, networks_container):
|
||||
addresses_node = xml_doc.createElement('addresses')
|
||||
for (network_id, ip_dicts) in networks_container.items():
|
||||
network_node = self._network_to_xml(xml_doc, network_id, ip_dicts)
|
||||
addresses_node.appendChild(network_node)
|
||||
return addresses_node
|
||||
|
||||
def show(self, network_container):
|
||||
(network_id, ip_dicts) = network_container.items()[0]
|
||||
xml_doc = minidom.Document()
|
||||
node = self._network_to_xml(xml_doc, network_id, ip_dicts)
|
||||
return self.to_xml_string(node, False)
|
||||
|
||||
def index(self, addresses_container):
|
||||
xml_doc = minidom.Document()
|
||||
node = self.networks_to_xml(xml_doc, addresses_container['addresses'])
|
||||
return self.to_xml_string(node, False)
|
||||
|
||||
|
||||
def create_resource(version):
|
||||
controller = {
|
||||
'1.0': ControllerV10,
|
||||
'1.1': ControllerV11,
|
||||
}[version]()
|
||||
|
||||
xmlns = {
|
||||
'1.0': wsgi.XMLNS_V10,
|
||||
'1.1': wsgi.XMLNS_V11,
|
||||
}[version]
|
||||
|
||||
metadata = {
|
||||
'list_collections': {
|
||||
'public': {'item_name': 'ip', 'item_key': 'addr'},
|
||||
|
@ -118,10 +153,11 @@ def create_resource(version):
|
|||
},
|
||||
}
|
||||
|
||||
body_serializers = {
|
||||
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=xmlns),
|
||||
}
|
||||
serializer = wsgi.ResponseSerializer(body_serializers)
|
||||
xml_serializer = {
|
||||
'1.0': wsgi.XMLDictSerializer(metadata=metadata, xmlns=wsgi.XMLNS_V11),
|
||||
'1.1': IPXMLSerializer(),
|
||||
}[version]
|
||||
|
||||
serializer = wsgi.ResponseSerializer({'application/xml': xml_serializer})
|
||||
|
||||
return wsgi.Resource(controller, serializer=serializer)
|
||||
|
|
|
@ -13,6 +13,7 @@ from nova import wsgi
|
|||
|
||||
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
|
||||
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
|
||||
XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
|
||||
|
||||
LOG = logging.getLogger('nova.api.openstack.wsgi')
|
||||
|
||||
|
@ -135,10 +136,44 @@ class XMLDeserializer(TextDeserializer):
|
|||
listnames)
|
||||
return result
|
||||
|
||||
def find_first_child_named(self, parent, name):
|
||||
"""Search a nodes children for the first child with a given name"""
|
||||
for node in parent.childNodes:
|
||||
if node.nodeName == name:
|
||||
return node
|
||||
return None
|
||||
|
||||
def find_children_named(self, parent, name):
|
||||
"""Return all of a nodes children who have the given name"""
|
||||
for node in parent.childNodes:
|
||||
if node.nodeName == name:
|
||||
yield node
|
||||
|
||||
def extract_text(self, node):
|
||||
"""Get the text field contained by the given node"""
|
||||
if len(node.childNodes) == 1:
|
||||
child = node.childNodes[0]
|
||||
if child.nodeType == child.TEXT_NODE:
|
||||
return child.nodeValue
|
||||
return ""
|
||||
|
||||
def default(self, datastring):
|
||||
return {'body': self._from_xml(datastring)}
|
||||
|
||||
|
||||
class MetadataXMLDeserializer(XMLDeserializer):
|
||||
|
||||
def extract_metadata(self, metadata_node):
|
||||
"""Marshal the metadata attribute of a parsed request"""
|
||||
if metadata_node is None:
|
||||
return None
|
||||
metadata = {}
|
||||
for meta_node in self.find_children_named(metadata_node, "meta"):
|
||||
key = meta_node.getAttribute("key")
|
||||
metadata[key] = self.extract_text(meta_node)
|
||||
return metadata
|
||||
|
||||
|
||||
class RequestHeadersDeserializer(ActionDispatcher):
|
||||
"""Default request headers deserializer"""
|
||||
|
||||
|
@ -396,8 +431,9 @@ class ResponseSerializer(object):
|
|||
|
||||
def serialize_body(self, response, data, content_type, action):
|
||||
response.headers['Content-Type'] = content_type
|
||||
serializer = self.get_body_serializer(content_type)
|
||||
response.body = serializer.serialize(data, action)
|
||||
if data is not None:
|
||||
serializer = self.get_body_serializer(content_type)
|
||||
response.body = serializer.serialize(data, action)
|
||||
|
||||
def get_body_serializer(self, content_type):
|
||||
try:
|
||||
|
@ -443,7 +479,7 @@ class Resource(wsgi.Application):
|
|||
action, args, accept = self.deserializer.deserialize(request)
|
||||
except exception.InvalidContentType:
|
||||
msg = _("Unsupported Content-Type")
|
||||
return webob.exc.HTTPBadRequest(explanation=msg)
|
||||
return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
|
||||
except exception.MalformedRequestBody:
|
||||
msg = _("Malformed request body")
|
||||
return faults.Fault(webob.exc.HTTPBadRequest(explanation=msg))
|
||||
|
@ -454,7 +490,6 @@ class Resource(wsgi.Application):
|
|||
LOG.info(_("HTTP exception thrown: %s"), unicode(ex))
|
||||
action_result = faults.Fault(ex)
|
||||
|
||||
#TODO(bcwaldon): find a more elegant way to pass through non-dict types
|
||||
if type(action_result) is dict or action_result is None:
|
||||
response = self.serializer.serialize(action_result,
|
||||
accept,
|
||||
|
|
|
@ -127,7 +127,7 @@ class API(base.Base):
|
|||
quota_metadata = quota.allowed_metadata_items(context, num_metadata)
|
||||
if quota_metadata < num_metadata:
|
||||
pid = context.project_id
|
||||
msg = _("Quota exceeeded for %(pid)s, tried to set "
|
||||
msg = _("Quota exceeded for %(pid)s, tried to set "
|
||||
"%(num_metadata)s metadata properties") % locals()
|
||||
LOG.warn(msg)
|
||||
raise quota.QuotaError(msg, "MetadataLimitExceeded")
|
||||
|
@ -138,7 +138,7 @@ class API(base.Base):
|
|||
for k, v in metadata.iteritems():
|
||||
if len(k) > 255 or len(v) > 255:
|
||||
pid = context.project_id
|
||||
msg = _("Quota exceeeded for %(pid)s, metadata property "
|
||||
msg = _("Quota exceeded for %(pid)s, metadata property "
|
||||
"key or value too long") % locals()
|
||||
LOG.warn(msg)
|
||||
raise quota.QuotaError(msg, "MetadataLimitExceeded")
|
||||
|
@ -165,7 +165,7 @@ class API(base.Base):
|
|||
instance_type)
|
||||
if num_instances < min_count:
|
||||
pid = context.project_id
|
||||
LOG.warn(_("Quota exceeeded for %(pid)s,"
|
||||
LOG.warn(_("Quota exceeded for %(pid)s,"
|
||||
" tried to run %(min_count)s instances") % locals())
|
||||
if num_instances <= 0:
|
||||
message = _("Instance quota exceeded. You cannot run any "
|
||||
|
|
|
@ -258,7 +258,7 @@ class FloatingIP(object):
|
|||
# NOTE(tr3buchet): all networks hosts in zone now use the same pool
|
||||
LOG.debug("QUOTA: %s" % quota.allowed_floating_ips(context, 1))
|
||||
if quota.allowed_floating_ips(context, 1) < 1:
|
||||
LOG.warn(_('Quota exceeeded for %s, tried to allocate '
|
||||
LOG.warn(_('Quota exceeded for %s, tried to allocate '
|
||||
'address'),
|
||||
context.project_id)
|
||||
raise quota.QuotaError(_('Address quota exceeded. You cannot '
|
||||
|
|
|
@ -247,3 +247,21 @@ class MiscFunctionsTest(test.TestCase):
|
|||
self.assertRaises(ValueError,
|
||||
common.get_id_from_href,
|
||||
fixture)
|
||||
|
||||
def test_get_version_from_href(self):
|
||||
fixture = 'http://www.testsite.com/v1.1/images'
|
||||
expected = '1.1'
|
||||
actual = common.get_version_from_href(fixture)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_get_version_from_href_2(self):
|
||||
fixture = 'http://www.testsite.com/v1.1'
|
||||
expected = '1.1'
|
||||
actual = common.get_version_from_href(fixture)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_get_version_from_href_default(self):
|
||||
fixture = 'http://www.testsite.com/images'
|
||||
expected = '1.0'
|
||||
actual = common.get_version_from_href(fixture)
|
||||
self.assertEqual(actual, expected)
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
# under the License.
|
||||
|
||||
import json
|
||||
import os.path
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
import os.path
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from nova import context
|
||||
from nova import flags
|
||||
|
@ -30,7 +31,8 @@ from nova.api.openstack import wsgi
|
|||
from nova.tests.api.openstack import fakes
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
NS = "{http://docs.openstack.org/compute/api/v1.1}"
|
||||
ATOMNS = "{http://www.w3.org/2005/Atom}"
|
||||
response_body = "Try to say this Mr. Knox, sir..."
|
||||
|
||||
|
||||
|
@ -80,20 +82,99 @@ class StubExtensionManager(object):
|
|||
|
||||
class ExtensionControllerTest(unittest.TestCase):
|
||||
|
||||
def test_index(self):
|
||||
def setUp(self):
|
||||
FLAGS.osapi_extensions_path = os.path.join(
|
||||
os.path.dirname(__file__), "extensions")
|
||||
|
||||
def test_list_extensions_json(self):
|
||||
app = openstack.APIRouterV11()
|
||||
ext_midware = extensions.ExtensionMiddleware(app)
|
||||
request = webob.Request.blank("/extensions")
|
||||
response = request.get_response(ext_midware)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
def test_get_by_alias(self):
|
||||
# Make sure we have all the extensions.
|
||||
data = json.loads(response.body)
|
||||
names = [x['name'] for x in data['extensions']]
|
||||
names.sort()
|
||||
self.assertEqual(names, ["FlavorExtraSpecs", "Floating_ips",
|
||||
"Fox In Socks", "Hosts", "Multinic", "Volumes"])
|
||||
|
||||
# Make sure that at least Fox in Sox is correct.
|
||||
(fox_ext,) = [
|
||||
x for x in data['extensions'] if x['alias'] == 'FOXNSOX']
|
||||
self.assertEqual(fox_ext, {
|
||||
'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0',
|
||||
'name': 'Fox In Socks',
|
||||
'updated': '2011-01-22T13:25:27-06:00',
|
||||
'description': 'The Fox In Socks Extension',
|
||||
'alias': 'FOXNSOX',
|
||||
'links': []
|
||||
}
|
||||
)
|
||||
|
||||
def test_get_extension_json(self):
|
||||
app = openstack.APIRouterV11()
|
||||
ext_midware = extensions.ExtensionMiddleware(app)
|
||||
request = webob.Request.blank("/extensions/FOXNSOX")
|
||||
response = request.get_response(ext_midware)
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
data = json.loads(response.body)
|
||||
self.assertEqual(data['extension'], {
|
||||
"namespace": "http://www.fox.in.socks/api/ext/pie/v1.0",
|
||||
"name": "Fox In Socks",
|
||||
"updated": "2011-01-22T13:25:27-06:00",
|
||||
"description": "The Fox In Socks Extension",
|
||||
"alias": "FOXNSOX",
|
||||
"links": []
|
||||
}
|
||||
)
|
||||
|
||||
def test_list_extensions_xml(self):
|
||||
app = openstack.APIRouterV11()
|
||||
ext_midware = extensions.ExtensionMiddleware(app)
|
||||
request = webob.Request.blank("/extensions")
|
||||
request.accept = "application/xml"
|
||||
response = request.get_response(ext_midware)
|
||||
self.assertEqual(200, response.status_int)
|
||||
print response.body
|
||||
|
||||
root = ElementTree.XML(response.body)
|
||||
self.assertEqual(root.tag.split('extensions')[0], NS)
|
||||
|
||||
# Make sure we have all the extensions.
|
||||
exts = root.findall('{0}extension'.format(NS))
|
||||
self.assertEqual(len(exts), 6)
|
||||
|
||||
# Make sure that at least Fox in Sox is correct.
|
||||
(fox_ext,) = [x for x in exts if x.get('alias') == 'FOXNSOX']
|
||||
self.assertEqual(fox_ext.get('name'), 'Fox In Socks')
|
||||
self.assertEqual(fox_ext.get('namespace'),
|
||||
'http://www.fox.in.socks/api/ext/pie/v1.0')
|
||||
self.assertEqual(fox_ext.get('updated'), '2011-01-22T13:25:27-06:00')
|
||||
self.assertEqual(fox_ext.findtext('{0}description'.format(NS)),
|
||||
'The Fox In Socks Extension')
|
||||
|
||||
def test_get_extension_xml(self):
|
||||
app = openstack.APIRouterV11()
|
||||
ext_midware = extensions.ExtensionMiddleware(app)
|
||||
request = webob.Request.blank("/extensions/FOXNSOX")
|
||||
request.accept = "application/xml"
|
||||
response = request.get_response(ext_midware)
|
||||
self.assertEqual(200, response.status_int)
|
||||
print response.body
|
||||
|
||||
root = ElementTree.XML(response.body)
|
||||
self.assertEqual(root.tag.split('extension')[0], NS)
|
||||
self.assertEqual(root.get('alias'), 'FOXNSOX')
|
||||
self.assertEqual(root.get('name'), 'Fox In Socks')
|
||||
self.assertEqual(root.get('namespace'),
|
||||
'http://www.fox.in.socks/api/ext/pie/v1.0')
|
||||
self.assertEqual(root.get('updated'), '2011-01-22T13:25:27-06:00')
|
||||
self.assertEqual(root.findtext('{0}description'.format(NS)),
|
||||
'The Fox In Socks Extension')
|
||||
|
||||
|
||||
class ResourceExtensionTest(unittest.TestCase):
|
||||
|
||||
|
@ -192,7 +273,7 @@ class ActionExtensionTest(unittest.TestCase):
|
|||
|
||||
def test_invalid_action(self):
|
||||
body = dict(blah=dict(name="test"))
|
||||
response = self._send_server_action_request("/asdf/1/action", body)
|
||||
response = self._send_server_action_request("/fdsa/1/action", body)
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
|
||||
|
@ -244,3 +325,109 @@ class RequestExtensionTest(unittest.TestCase):
|
|||
response_data = json.loads(response.body)
|
||||
self.assertEqual('newblue', response_data['flavor']['googoose'])
|
||||
self.assertEqual("Pig Bands!", response_data['big_bands'])
|
||||
|
||||
|
||||
class ExtensionsXMLSerializerTest(unittest.TestCase):
|
||||
|
||||
def test_serialize_extenstion(self):
|
||||
serializer = extensions.ExtensionsXMLSerializer()
|
||||
data = {
|
||||
'extension': {
|
||||
'name': 'ext1',
|
||||
'namespace': 'http://docs.rack.com/servers/api/ext/pie/v1.0',
|
||||
'alias': 'RS-PIE',
|
||||
'updated': '2011-01-22T13:25:27-06:00',
|
||||
'description': 'Adds the capability to share an image.',
|
||||
'links': [
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'application/pdf',
|
||||
'href': 'http://docs.rack.com/servers/api/ext/cs.pdf'
|
||||
},
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'application/vnd.sun.wadl+xml',
|
||||
'href': 'http://docs.rack.com/servers/api/ext/cs.wadl'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
xml = serializer.serialize(data, 'show')
|
||||
root = ElementTree.XML(xml)
|
||||
ext_dict = data['extension']
|
||||
self.assertEqual(root.findtext('{0}description'.format(NS)),
|
||||
ext_dict['description'])
|
||||
|
||||
for key in ['name', 'namespace', 'alias', 'updated']:
|
||||
self.assertEqual(root.get(key), ext_dict[key])
|
||||
|
||||
link_nodes = root.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(ext_dict['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
||||
def test_serialize_extensions(self):
|
||||
serializer = extensions.ExtensionsXMLSerializer()
|
||||
data = {
|
||||
"extensions": [
|
||||
{
|
||||
"name": "Public Image Extension",
|
||||
"namespace": "http://foo.com/api/ext/pie/v1.0",
|
||||
"alias": "RS-PIE",
|
||||
"updated": "2011-01-22T13:25:27-06:00",
|
||||
"description": "Adds the capability to share an image.",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://foo.com/api/ext/cs-pie.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://foo.com/api/ext/cs-pie.wadl"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Cloud Block Storage",
|
||||
"namespace": "http://foo.com/api/ext/cbs/v1.0",
|
||||
"alias": "RS-CBS",
|
||||
"updated": "2011-01-12T11:22:33-06:00",
|
||||
"description": "Allows mounting cloud block storage.",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://foo.com/api/ext/cs-cbs.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://foo.com/api/ext/cs-cbs.wadl"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
xml = serializer.serialize(data, 'index')
|
||||
print xml
|
||||
root = ElementTree.XML(xml)
|
||||
ext_elems = root.findall('{0}extension'.format(NS))
|
||||
self.assertEqual(len(ext_elems), 2)
|
||||
for i, ext_elem in enumerate(ext_elems):
|
||||
ext_dict = data['extensions'][i]
|
||||
self.assertEqual(ext_elem.findtext('{0}description'.format(NS)),
|
||||
ext_dict['description'])
|
||||
|
||||
for key in ['name', 'namespace', 'alias', 'updated']:
|
||||
self.assertEqual(ext_elem.get(key), ext_dict[key])
|
||||
|
||||
link_nodes = ext_elem.findall('{0}link'.format(ATOMNS))
|
||||
self.assertEqual(len(link_nodes), 2)
|
||||
for i, link in enumerate(ext_dict['links']):
|
||||
for key, value in link.items():
|
||||
self.assertEqual(link_nodes[i].get(key), value)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
# under the License.
|
||||
|
||||
import json
|
||||
from xml.dom import minidom
|
||||
|
||||
import webob
|
||||
import webob.dec
|
||||
|
@ -24,6 +25,7 @@ import webob.exc
|
|||
from nova import test
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import faults
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
class TestFaults(test.TestCase):
|
||||
|
@ -144,3 +146,108 @@ class TestFaults(test.TestCase):
|
|||
"""Ensure the status_int is set correctly on faults"""
|
||||
fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='what?'))
|
||||
self.assertEqual(fault.status_int, 400)
|
||||
|
||||
def test_v10_xml_serializer(self):
|
||||
"""Ensure that a v1.0 request responds with a v1.0 xmlns"""
|
||||
request = webob.Request.blank('/',
|
||||
headers={"Accept": "application/xml"})
|
||||
|
||||
fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
|
||||
response = request.get_response(fault)
|
||||
|
||||
self.assertTrue(common.XML_NS_V10 in response.body)
|
||||
self.assertEqual(response.content_type, "application/xml")
|
||||
self.assertEqual(response.status_int, 400)
|
||||
|
||||
def test_v11_xml_serializer(self):
|
||||
"""Ensure that a v1.1 request responds with a v1.1 xmlns"""
|
||||
request = webob.Request.blank('/v1.1',
|
||||
headers={"Accept": "application/xml"})
|
||||
|
||||
fault = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
|
||||
response = request.get_response(fault)
|
||||
|
||||
self.assertTrue(common.XML_NS_V11 in response.body)
|
||||
self.assertEqual(response.content_type, "application/xml")
|
||||
self.assertEqual(response.status_int, 400)
|
||||
|
||||
|
||||
class FaultsXMLSerializationTestV11(test.TestCase):
|
||||
"""Tests covering `nova.api.openstack.faults:Fault` class."""
|
||||
|
||||
def _prepare_xml(self, xml_string):
|
||||
xml_string = xml_string.replace(" ", "")
|
||||
xml_string = xml_string.replace("\n", "")
|
||||
xml_string = xml_string.replace("\t", "")
|
||||
return xml_string
|
||||
|
||||
def test_400_fault(self):
|
||||
metadata = {'attributes': {"badRequest": 'code'}}
|
||||
serializer = wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=common.XML_NS_V11)
|
||||
|
||||
fixture = {
|
||||
"badRequest": {
|
||||
"message": "scram",
|
||||
"code": 400,
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture)
|
||||
actual = minidom.parseString(self._prepare_xml(output))
|
||||
|
||||
expected = minidom.parseString(self._prepare_xml("""
|
||||
<badRequest code="400" xmlns="%s">
|
||||
<message>scram</message>
|
||||
</badRequest>
|
||||
""") % common.XML_NS_V11)
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_413_fault(self):
|
||||
metadata = {'attributes': {"overLimit": 'code'}}
|
||||
serializer = wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=common.XML_NS_V11)
|
||||
|
||||
fixture = {
|
||||
"overLimit": {
|
||||
"message": "sorry",
|
||||
"code": 413,
|
||||
"retryAfter": 4,
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture)
|
||||
actual = minidom.parseString(self._prepare_xml(output))
|
||||
|
||||
expected = minidom.parseString(self._prepare_xml("""
|
||||
<overLimit code="413" xmlns="%s">
|
||||
<message>sorry</message>
|
||||
<retryAfter>4</retryAfter>
|
||||
</overLimit>
|
||||
""") % common.XML_NS_V11)
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_404_fault(self):
|
||||
metadata = {'attributes': {"itemNotFound": 'code'}}
|
||||
serializer = wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=common.XML_NS_V11)
|
||||
|
||||
fixture = {
|
||||
"itemNotFound": {
|
||||
"message": "sorry",
|
||||
"code": 404,
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture)
|
||||
actual = minidom.parseString(self._prepare_xml(output))
|
||||
|
||||
expected = minidom.parseString(self._prepare_xml("""
|
||||
<itemNotFound code="404" xmlns="%s">
|
||||
<message>sorry</message>
|
||||
</itemNotFound>
|
||||
""") % common.XML_NS_V11)
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
|
|
@ -103,8 +103,7 @@ class ImageMetaDataTest(test.TestCase):
|
|||
super(ImageMetaDataTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
@ -114,8 +113,7 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual(value, res_dict['metadata'][key])
|
||||
|
||||
def test_show(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/key1')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key1')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
@ -124,42 +122,66 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual('value1', res_dict['meta']['key1'])
|
||||
|
||||
def test_show_not_found(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/key9')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key9')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(404, res.status_int)
|
||||
|
||||
def test_create(self):
|
||||
req = webob.Request.blank('/v1.1/images/2/meta')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/2/metadata')
|
||||
req.method = 'POST'
|
||||
req.body = '{"metadata": {"key9": "value9"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('value9', res_dict['metadata']['key9'])
|
||||
# other items should not be modified
|
||||
self.assertEqual('value1', res_dict['metadata']['key1'])
|
||||
self.assertEqual('value2', res_dict['metadata']['key2'])
|
||||
self.assertEqual(1, len(res_dict))
|
||||
actual_output = json.loads(res.body)
|
||||
|
||||
expected_output = {
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
'key2': 'value2',
|
||||
'key9': 'value9',
|
||||
},
|
||||
}
|
||||
|
||||
self.assertEqual(expected_output, actual_output)
|
||||
|
||||
def test_update_all(self):
|
||||
req = webob.Request.blank('/v1.1/images/2/metadata')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"metadata": {"key9": "value9"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
actual_output = json.loads(res.body)
|
||||
|
||||
expected_output = {
|
||||
'metadata': {
|
||||
'key9': 'value9',
|
||||
},
|
||||
}
|
||||
|
||||
self.assertEqual(expected_output, actual_output)
|
||||
|
||||
def test_update_item(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/key1')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"meta": {"key1": "zz"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertTrue('meta' in res_dict)
|
||||
self.assertEqual(len(res_dict['meta']), 1)
|
||||
self.assertEqual('zz', res_dict['meta']['key1'])
|
||||
actual_output = json.loads(res.body)
|
||||
expected_output = {
|
||||
'meta': {
|
||||
'key1': 'zz',
|
||||
},
|
||||
}
|
||||
self.assertEqual(actual_output, expected_output)
|
||||
|
||||
def test_update_item_bad_body(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/key1')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "zz"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
@ -167,8 +189,7 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/key1')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"meta": {"key1": "value1", "key2": "value2"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
@ -176,24 +197,38 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/meta/bad')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/bad')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"meta": {"key1": "value1"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_xml(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '<meta key="key1">five</meta>'
|
||||
req.headers["content-type"] = "application/xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
self.assertEqual(200, res.status_int)
|
||||
actual_output = json.loads(res.body)
|
||||
expected_output = {
|
||||
'meta': {
|
||||
'key1': 'five',
|
||||
},
|
||||
}
|
||||
self.assertEqual(actual_output, expected_output)
|
||||
|
||||
def test_delete(self):
|
||||
req = webob.Request.blank('/v1.1/images/2/meta/key1')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/2/metadata/key1')
|
||||
req.method = 'DELETE'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual(204, res.status_int)
|
||||
self.assertEqual('', res.body)
|
||||
|
||||
def test_delete_not_found(self):
|
||||
req = webob.Request.blank('/v1.1/images/2/meta/blah')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/2/metadata/blah')
|
||||
req.method = 'DELETE'
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(404, res.status_int)
|
||||
|
@ -203,8 +238,7 @@ class ImageMetaDataTest(test.TestCase):
|
|||
for num in range(FLAGS.quota_metadata_items + 1):
|
||||
data['metadata']['key%i' % num] = "blah"
|
||||
json_string = str(data).replace("\'", "\"")
|
||||
req = webob.Request.blank('/v1.1/images/2/meta')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/2/metadata')
|
||||
req.method = 'POST'
|
||||
req.body = json_string
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
@ -212,8 +246,7 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_too_many_metadata_items_on_put(self):
|
||||
req = webob.Request.blank('/v1.1/images/3/meta/blah')
|
||||
req.environ['api.version'] = '1.1'
|
||||
req = webob.Request.blank('/v1.1/images/3/metadata/blah')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"meta": {"blah": "blah"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
|
@ -221,9 +254,49 @@ class ImageMetaDataTest(test.TestCase):
|
|||
self.assertEqual(400, res.status_int)
|
||||
|
||||
|
||||
class ImageMetadataXMLDeserializationTest(test.TestCase):
|
||||
|
||||
deserializer = openstack.image_metadata.ImageMetadataXMLDeserializer()
|
||||
|
||||
def test_create(self):
|
||||
request_body = """
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key='123'>asdf</meta>
|
||||
<meta key='567'>jkl;</meta>
|
||||
</metadata>"""
|
||||
output = self.deserializer.deserialize(request_body, 'create')
|
||||
expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
|
||||
self.assertEquals(output, expected)
|
||||
|
||||
def test_create_empty(self):
|
||||
request_body = """
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1"/>"""
|
||||
output = self.deserializer.deserialize(request_body, 'create')
|
||||
expected = {"body": {"metadata": {}}}
|
||||
self.assertEquals(output, expected)
|
||||
|
||||
def test_update_all(self):
|
||||
request_body = """
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key='123'>asdf</meta>
|
||||
<meta key='567'>jkl;</meta>
|
||||
</metadata>"""
|
||||
output = self.deserializer.deserialize(request_body, 'update_all')
|
||||
expected = {"body": {"metadata": {"123": "asdf", "567": "jkl;"}}}
|
||||
self.assertEquals(output, expected)
|
||||
|
||||
def test_update(self):
|
||||
request_body = """
|
||||
<meta xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
key='123'>asdf</meta>"""
|
||||
output = self.deserializer.deserialize(request_body, 'update')
|
||||
expected = {"body": {"meta": {"123": "asdf"}}}
|
||||
self.assertEquals(output, expected)
|
||||
|
||||
|
||||
class ImageMetadataXMLSerializationTest(test.TestCase):
|
||||
|
||||
def test_index_xml(self):
|
||||
def test_index(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
|
@ -247,7 +320,7 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_xml_null(self):
|
||||
def test_index_null(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
|
@ -267,7 +340,7 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_xml_unicode(self):
|
||||
def test_index_unicode(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
|
@ -287,7 +360,7 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_show_xml(self):
|
||||
def test_show(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'meta': {
|
||||
|
@ -305,7 +378,31 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_update_item_xml(self):
|
||||
def test_update_all(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
'key6': 'value6',
|
||||
'key4': 'value4',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'update_all')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="key6">
|
||||
value6
|
||||
</meta>
|
||||
<meta key="key4">
|
||||
value4
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_update_item(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'meta': {
|
||||
|
@ -323,7 +420,7 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_create_xml(self):
|
||||
def test_create(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
|
@ -350,3 +447,8 @@ class ImageMetadataXMLSerializationTest(test.TestCase):
|
|||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_delete(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
output = serializer.serialize(None, 'delete')
|
||||
self.assertEqual(output, '')
|
||||
|
|
|
@ -538,7 +538,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
|||
# because the element hasn't changed definition
|
||||
expected = minidom.parseString("""
|
||||
<itemNotFound code="404"
|
||||
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
|
||||
xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<message>
|
||||
Image not found.
|
||||
</message>
|
||||
|
|
|
@ -30,8 +30,9 @@ from nova import flags
|
|||
from nova import test
|
||||
from nova import utils
|
||||
import nova.api.openstack
|
||||
from nova.api.openstack import servers
|
||||
from nova.api.openstack import create_instance_helper
|
||||
from nova.api.openstack import servers
|
||||
from nova.api.openstack import wsgi
|
||||
import nova.compute.api
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
|
@ -941,6 +942,18 @@ class ServersTest(test.TestCase):
|
|||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_create_instance_no_server_entity(self):
|
||||
self._setup_for_create_instance()
|
||||
|
||||
body = {}
|
||||
|
||||
req = webob.Request.blank('/v1.0/servers')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 422)
|
||||
|
||||
def test_create_instance_whitespace_name(self):
|
||||
self._setup_for_create_instance()
|
||||
|
||||
|
@ -2188,6 +2201,62 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
|
|||
"http://localhost:8774/v1.1/images/1")
|
||||
|
||||
|
||||
class TextAddressesXMLSerialization(test.TestCase):
|
||||
|
||||
serializer = nova.api.openstack.ips.IPXMLSerializer()
|
||||
|
||||
def test_show(self):
|
||||
fixture = {
|
||||
'network_2': [
|
||||
{'addr': '192.168.0.1', 'version': 4},
|
||||
{'addr': 'fe80::beef', 'version': 6},
|
||||
],
|
||||
}
|
||||
output = self.serializer.serialize(fixture, 'show')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<network xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
id="network_2">
|
||||
<ip version="4" addr="192.168.0.1"/>
|
||||
<ip version="6" addr="fe80::beef"/>
|
||||
</network>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index(self):
|
||||
fixture = {
|
||||
'addresses': {
|
||||
'network_1': [
|
||||
{'addr': '192.168.0.3', 'version': 4},
|
||||
{'addr': '192.168.0.5', 'version': 4},
|
||||
],
|
||||
'network_2': [
|
||||
{'addr': '192.168.0.1', 'version': 4},
|
||||
{'addr': 'fe80::beef', 'version': 6},
|
||||
],
|
||||
},
|
||||
}
|
||||
output = self.serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<addresses xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<network id="network_2">
|
||||
<ip version="4" addr="192.168.0.1"/>
|
||||
<ip version="6" addr="fe80::beef"/>
|
||||
</network>
|
||||
<network id="network_1">
|
||||
<ip version="4" addr="192.168.0.3"/>
|
||||
<ip version="4" addr="192.168.0.5"/>
|
||||
</network>
|
||||
</addresses>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
|
||||
class TestServerInstanceCreation(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -256,6 +256,13 @@ class ResponseSerializerTest(test.TestCase):
|
|||
self.assertEqual(response.body, 'pew_json')
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_serialize_response_None(self):
|
||||
response = self.serializer.serialize(None, 'application/json')
|
||||
print response
|
||||
self.assertEqual(response.headers['Content-Type'], 'application/json')
|
||||
self.assertEqual(response.body, '')
|
||||
self.assertEqual(response.status_int, 404)
|
||||
|
||||
def test_serialize_response_dict_to_unknown_content_type(self):
|
||||
self.assertRaises(exception.InvalidContentType,
|
||||
self.serializer.serialize,
|
||||
|
|
|
@ -269,25 +269,64 @@ class CloudTestCase(test.TestCase):
|
|||
delete = self.cloud.delete_security_group
|
||||
self.assertRaises(exception.ApiError, delete, self.context)
|
||||
|
||||
def test_authorize_revoke_security_group_ingress(self):
|
||||
def test_authorize_security_group_ingress(self):
|
||||
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
|
||||
sec = db.security_group_create(self.context, kwargs)
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
|
||||
authz(self.context, group_name=sec['name'], **kwargs)
|
||||
self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
|
||||
|
||||
def test_authorize_security_group_ingress_ip_permissions_ip_ranges(self):
|
||||
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
|
||||
sec = db.security_group_create(self.context, kwargs)
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
|
||||
'ip_ranges':
|
||||
{'1': {'cidr_ip': u'0.0.0.0/0'},
|
||||
'2': {'cidr_ip': u'10.10.10.10/32'}},
|
||||
'ip_protocol': u'tcp'}]}
|
||||
self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
|
||||
|
||||
def test_authorize_security_group_ingress_ip_permissions_groups(self):
|
||||
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
|
||||
sec = db.security_group_create(self.context, kwargs)
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'ip_permissions': [{'to_port': 81, 'from_port': 81,
|
||||
'ip_ranges':{'1': {'cidr_ip': u'0.0.0.0/0'},
|
||||
'2': {'cidr_ip': u'10.10.10.10/32'}},
|
||||
'groups': {'1': {'user_id': u'someuser',
|
||||
'group_name': u'somegroup1'},
|
||||
'2': {'user_id': u'someuser',
|
||||
'group_name': u'othergroup2'}},
|
||||
'ip_protocol': u'tcp'}]}
|
||||
self.assertTrue(authz(self.context, group_name=sec['name'], **kwargs))
|
||||
|
||||
def test_revoke_security_group_ingress(self):
|
||||
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
|
||||
sec = db.security_group_create(self.context, kwargs)
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
|
||||
authz(self.context, group_id=sec['id'], **kwargs)
|
||||
revoke = self.cloud.revoke_security_group_ingress
|
||||
self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs))
|
||||
|
||||
def test_authorize_revoke_security_group_ingress_by_id(self):
|
||||
sec = db.security_group_create(self.context,
|
||||
{'project_id': self.context.project_id,
|
||||
'name': 'test'})
|
||||
def test_revoke_security_group_ingress_by_id(self):
|
||||
kwargs = {'project_id': self.context.project_id, 'name': 'test'}
|
||||
sec = db.security_group_create(self.context, kwargs)
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
|
||||
authz(self.context, group_id=sec['id'], **kwargs)
|
||||
revoke = self.cloud.revoke_security_group_ingress
|
||||
self.assertTrue(revoke(self.context, group_id=sec['id'], **kwargs))
|
||||
|
||||
def test_authorize_security_group_ingress_by_id(self):
|
||||
sec = db.security_group_create(self.context,
|
||||
{'project_id': self.context.project_id,
|
||||
'name': 'test'})
|
||||
authz = self.cloud.authorize_security_group_ingress
|
||||
kwargs = {'to_port': '999', 'from_port': '999', 'ip_protocol': 'tcp'}
|
||||
self.assertTrue(authz(self.context, group_id=sec['id'], **kwargs))
|
||||
|
||||
def test_authorize_security_group_ingress_missing_protocol_params(self):
|
||||
sec = db.security_group_create(self.context,
|
||||
{'project_id': self.context.project_id,
|
||||
|
@ -908,6 +947,21 @@ class CloudTestCase(test.TestCase):
|
|||
self._wait_for_running(ec2_instance_id)
|
||||
return ec2_instance_id
|
||||
|
||||
def test_rescue_unrescue_instance(self):
|
||||
instance_id = self._run_instance(
|
||||
image_id='ami-1',
|
||||
instance_type=FLAGS.default_instance_type,
|
||||
max_count=1)
|
||||
self.cloud.rescue_instance(context=self.context,
|
||||
instance_id=instance_id)
|
||||
# NOTE(vish): This currently does no validation, it simply makes sure
|
||||
# that the code path doesn't throw an exception.
|
||||
self.cloud.unrescue_instance(context=self.context,
|
||||
instance_id=instance_id)
|
||||
# TODO(soren): We need this until we can stop polling in the rpc code
|
||||
# for unit tests.
|
||||
self.cloud.terminate_instances(self.context, [instance_id])
|
||||
|
||||
def test_console_output(self):
|
||||
instance_id = self._run_instance(
|
||||
image_id='ami-1',
|
||||
|
|
|
@ -82,9 +82,13 @@
|
|||
</disk>
|
||||
#end if
|
||||
#for $vol in $volumes
|
||||
<disk type='block'>
|
||||
<disk type='${vol.type}'>
|
||||
<driver type='raw'/>
|
||||
#if $vol.type == 'network'
|
||||
<source protocol='${vol.protocol}' name='${vol.name}'/>
|
||||
#else
|
||||
<source dev='${vol.device_path}'/>
|
||||
#end if
|
||||
<target dev='${vol.mount_device}' bus='${disk_bus}'/>
|
||||
</disk>
|
||||
#end for
|
||||
|
|
|
@ -335,21 +335,20 @@ class LibvirtConnection(driver.ComputeDriver):
|
|||
def attach_volume(self, instance_name, device_path, mountpoint):
|
||||
virt_dom = self._lookup_by_name(instance_name)
|
||||
mount_device = mountpoint.rpartition("/")[2]
|
||||
if device_path.startswith('/dev/'):
|
||||
(type, protocol, name) = \
|
||||
self._get_volume_device_info(vol['device_path'])
|
||||
if type == 'block':
|
||||
xml = """<disk type='block'>
|
||||
<driver name='qemu' type='raw'/>
|
||||
<source dev='%s'/>
|
||||
<target dev='%s' bus='virtio'/>
|
||||
</disk>""" % (device_path, mount_device)
|
||||
elif ':' in device_path:
|
||||
(protocol, name) = device_path.split(':')
|
||||
elif type == 'network':
|
||||
xml = """<disk type='network'>
|
||||
<driver name='qemu' type='raw'/>
|
||||
<source protocol='%s' name='%s'/>
|
||||
<target dev='%s' bus='virtio'/>
|
||||
</disk>""" % (protocol,
|
||||
name,
|
||||
mount_device)
|
||||
</disk>""" % (protocol, name, mount_device)
|
||||
else:
|
||||
raise exception.InvalidDevicePath(path=device_path)
|
||||
|
||||
|
@ -973,6 +972,16 @@ class LibvirtConnection(driver.ComputeDriver):
|
|||
return True
|
||||
return False
|
||||
|
||||
@exception.wrap_exception
|
||||
def _get_volume_device_info(self, device_path):
|
||||
if device_path.startswith('/dev/'):
|
||||
return ('block', None, None)
|
||||
elif ':' in device_path:
|
||||
(protocol, name) = device_path.split(':')
|
||||
return ('network', protocol, name)
|
||||
else:
|
||||
raise exception.InvalidDevicePath(path=device_path)
|
||||
|
||||
def _prepare_xml_info(self, instance, rescue=False, network_info=None,
|
||||
block_device_mapping=None):
|
||||
block_device_mapping = block_device_mapping or []
|
||||
|
@ -995,6 +1004,9 @@ class LibvirtConnection(driver.ComputeDriver):
|
|||
|
||||
for vol in block_device_mapping:
|
||||
vol['mount_device'] = _strip_dev(vol['mount_device'])
|
||||
(vol['type'], vol['protocol'], vol['name']) = \
|
||||
self._get_volume_device_info(vol['device_path'])
|
||||
|
||||
ebs_root = self._volume_in_mapping(self.root_mount_device,
|
||||
block_device_mapping)
|
||||
if self._volume_in_mapping(self.local_mount_device,
|
||||
|
|
|
@ -45,10 +45,30 @@ def get_network_with_the_name(session, network_name="vmnet0"):
|
|||
networks = session._call_method(vim_util,
|
||||
"get_properties_for_a_collection_of_objects",
|
||||
"Network", vm_networks, ["summary.name"])
|
||||
for network in networks:
|
||||
if network.propSet[0].val == network_name:
|
||||
return network.obj
|
||||
return None
|
||||
network_obj = {}
|
||||
for network in vm_networks:
|
||||
# Get network properties
|
||||
if network._type == 'DistributedVirtualPortgroup':
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"DistributedVirtualPortgroup", "config")
|
||||
# NOTE(asomya): This only works on ESXi if the port binding is
|
||||
# set to ephemeral
|
||||
if props.name == network_name:
|
||||
network_obj['type'] = 'DistributedVirtualPortgroup'
|
||||
network_obj['dvpg'] = props.key
|
||||
network_obj['dvsw'] = props.distributedVirtualSwitch.value
|
||||
else:
|
||||
props = session._call_method(vim_util,
|
||||
"get_dynamic_property", network,
|
||||
"Network", "summary.name")
|
||||
if props == network_name:
|
||||
network_obj['type'] = 'Network'
|
||||
network_obj['name'] = network_name
|
||||
if (len(network_obj) > 0):
|
||||
return network_obj
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_vswitch_for_vlan_interface(session, vlan_interface):
|
||||
|
|
|
@ -40,7 +40,7 @@ def split_datastore_path(datastore_path):
|
|||
|
||||
def get_vm_create_spec(client_factory, instance, data_store_name,
|
||||
network_name="vmnet0",
|
||||
os_type="otherGuest"):
|
||||
os_type="otherGuest", network_ref=None):
|
||||
"""Builds the VM Create spec."""
|
||||
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
config_spec.name = instance.name
|
||||
|
@ -93,7 +93,8 @@ def create_controller_spec(client_factory, key):
|
|||
return virtual_device_config
|
||||
|
||||
|
||||
def create_network_spec(client_factory, network_name, mac_address):
|
||||
def create_network_spec(client_factory, network_name, mac_address,
|
||||
network_ref=None):
|
||||
"""
|
||||
Builds a config spec for the addition of a new network
|
||||
adapter to the VM.
|
||||
|
@ -105,9 +106,24 @@ def create_network_spec(client_factory, network_name, mac_address):
|
|||
# Get the recommended card type for the VM based on the guest OS of the VM
|
||||
net_device = client_factory.create('ns0:VirtualPCNet32')
|
||||
|
||||
backing = \
|
||||
client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
|
||||
backing.deviceName = network_name
|
||||
# NOTE(asomya): Only works on ESXi if the portgroup binding is set to
|
||||
# ephemeral. Invalid configuration if set to static and the NIC does
|
||||
# not come up on boot if set to dynamic.
|
||||
backing = None
|
||||
if (network_ref['type'] == "DistributedVirtualPortgroup"):
|
||||
backing_name = \
|
||||
'ns0:VirtualEthernetCardDistributedVirtualPortBackingInfo'
|
||||
backing = \
|
||||
client_factory.create(backing_name)
|
||||
portgroup = \
|
||||
client_factory.create('ns0:DistributedVirtualSwitchPortConnection')
|
||||
portgroup.switchUuid = network_ref['dvsw']
|
||||
portgroup.portgroupKey = network_ref['dvpg']
|
||||
backing.port = portgroup
|
||||
else:
|
||||
backing = \
|
||||
client_factory.create('ns0:VirtualEthernetCardNetworkBackingInfo')
|
||||
backing.deviceName = network_name
|
||||
|
||||
connectable_spec = \
|
||||
client_factory.create('ns0:VirtualDeviceConnectInfo')
|
||||
|
@ -278,9 +294,11 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name):
|
|||
return config_spec
|
||||
|
||||
|
||||
def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, gateway):
|
||||
def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask,
|
||||
gateway, broadcast, dns):
|
||||
"""Builds the machine id change config spec."""
|
||||
machine_id_str = "%s;%s;%s;%s" % (mac, ip_addr, netmask, gateway)
|
||||
machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask,
|
||||
gateway, broadcast, dns)
|
||||
virtual_machine_config_spec = \
|
||||
client_factory.create('ns0:VirtualMachineConfigSpec')
|
||||
|
||||
|
|
|
@ -116,8 +116,9 @@ class VMWareVMOps(object):
|
|||
net_name)
|
||||
if network_ref is None:
|
||||
raise exception.NetworkNotFoundForBridge(bridge=net_name)
|
||||
return network_ref
|
||||
|
||||
_check_if_network_bridge_exists()
|
||||
network_obj = _check_if_network_bridge_exists()
|
||||
|
||||
def _get_datastore_ref():
|
||||
"""Get the datastore list and choose the first local storage."""
|
||||
|
@ -175,8 +176,10 @@ class VMWareVMOps(object):
|
|||
vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors()
|
||||
|
||||
# Get the create vm config spec
|
||||
config_spec = vm_util.get_vm_create_spec(client_factory, instance,
|
||||
data_store_name, net_name, os_type)
|
||||
config_spec = vm_util.get_vm_create_spec(
|
||||
client_factory, instance,
|
||||
data_store_name, net_name, os_type,
|
||||
network_obj)
|
||||
|
||||
def _execute_create_vm():
|
||||
"""Create VM on ESX host."""
|
||||
|
@ -718,13 +721,17 @@ class VMWareVMOps(object):
|
|||
|
||||
net_mask = network["netmask"]
|
||||
gateway = network["gateway"]
|
||||
broadcast = network["broadcast"]
|
||||
dns = network["dns"]
|
||||
|
||||
addresses = db.instance_get_fixed_addresses(admin_context,
|
||||
instance['id'])
|
||||
ip_addr = addresses[0] if addresses else None
|
||||
|
||||
machine_id_chanfge_spec = \
|
||||
vm_util.get_machine_id_change_spec(client_factory, mac_address,
|
||||
ip_addr, net_mask, gateway)
|
||||
ip_addr, net_mask, gateway,
|
||||
broadcast, dns)
|
||||
LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id "
|
||||
"with ip - %(ip_addr)s") %
|
||||
({'name': instance.name,
|
||||
|
|
|
@ -52,7 +52,7 @@ class API(base.Base):
|
|||
|
||||
if quota.allowed_volumes(context, 1, size) < 1:
|
||||
pid = context.project_id
|
||||
LOG.warn(_("Quota exceeeded for %(pid)s, tried to create"
|
||||
LOG.warn(_("Quota exceeded for %(pid)s, tried to create"
|
||||
" %(size)sG volume") % locals())
|
||||
raise quota.QuotaError(_("Volume quota exceeded. You cannot "
|
||||
"create a volume of size %sG") % size)
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:18+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
4
po/cs.po
4
po/cs.po
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:18+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
4
po/da.po
4
po/da.po
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:18+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
49
po/de.po
49
po/de.po
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: nova\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-02-21 10:03-0500\n"
|
||||
"PO-Revision-Date: 2011-04-03 19:42+0000\n"
|
||||
"Last-Translator: Matthias Loidolt <kedapperdrake@googlemail.com>\n"
|
||||
"PO-Revision-Date: 2011-06-06 07:58+0000\n"
|
||||
"Last-Translator: Christian Berendt <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-04-04 05:19+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
@ -85,6 +85,7 @@ msgstr ""
|
|||
#, python-format
|
||||
msgid "%(param)s property not found for image %(_image_id)s"
|
||||
msgstr ""
|
||||
"Die Property %(param)s konnte im Image %(_image_id)s nicht gefunden werden"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:168
|
||||
msgid "No keypairs defined"
|
||||
|
@ -141,12 +142,12 @@ msgstr "PID-Datei %s existiert nicht. Läuft der Daemon nicht?\n"
|
|||
|
||||
#: ../nova/twistd.py:221
|
||||
msgid "No such process"
|
||||
msgstr ""
|
||||
msgstr "Kein passender Prozess gefunden"
|
||||
|
||||
#: ../nova/twistd.py:230 ../nova/service.py:224
|
||||
#, python-format
|
||||
msgid "Serving %s"
|
||||
msgstr ""
|
||||
msgstr "Bedient %s"
|
||||
|
||||
#: ../nova/twistd.py:262 ../nova/service.py:225
|
||||
msgid "Full set of FLAGS:"
|
||||
|
@ -183,12 +184,13 @@ msgstr ""
|
|||
#: ../nova/virt/xenapi/volumeops.py:91
|
||||
#, python-format
|
||||
msgid "Unable to attach volume to instance %s"
|
||||
msgstr ""
|
||||
msgstr "Nicht möglich Volumen zur Instanze %s hinzuzufügen"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:93
|
||||
#, python-format
|
||||
msgid "Mountpoint %(mountpoint)s attached to instance %(instance_name)s"
|
||||
msgstr ""
|
||||
"Einhängepunkt%(mountpoint)s zur Instanze %(instance_name)s hinzugefügt"
|
||||
|
||||
#. Detach VBD from VM
|
||||
#: ../nova/virt/xenapi/volumeops.py:104
|
||||
|
@ -199,7 +201,7 @@ msgstr ""
|
|||
#: ../nova/virt/xenapi/volumeops.py:112
|
||||
#, python-format
|
||||
msgid "Unable to locate volume %s"
|
||||
msgstr ""
|
||||
msgstr "Nicht möglich volume %s zufinden"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:120
|
||||
#, python-format
|
||||
|
@ -214,7 +216,7 @@ msgstr ""
|
|||
#: ../nova/compute/instance_types.py:41
|
||||
#, python-format
|
||||
msgid "Unknown instance type: %s"
|
||||
msgstr ""
|
||||
msgstr "Unbekannter Instanztyp: %s"
|
||||
|
||||
#: ../nova/crypto.py:46
|
||||
msgid "Filename of root CA"
|
||||
|
@ -230,7 +232,7 @@ msgstr "Dateiname der Certificate Revocation List"
|
|||
|
||||
#: ../nova/crypto.py:53
|
||||
msgid "Where we keep our keys"
|
||||
msgstr ""
|
||||
msgstr "Wo wir unsere Schlüssel aufbewahren"
|
||||
|
||||
#: ../nova/crypto.py:55
|
||||
msgid "Where we keep our root CA"
|
||||
|
@ -298,12 +300,12 @@ msgstr ""
|
|||
|
||||
#: ../nova/compute/manager.py:179
|
||||
msgid "Instance has already been created"
|
||||
msgstr ""
|
||||
msgstr "Instanz wurde bereits erstellt"
|
||||
|
||||
#: ../nova/compute/manager.py:180
|
||||
#, python-format
|
||||
msgid "instance %s: starting..."
|
||||
msgstr ""
|
||||
msgstr "Instanz %s startet..."
|
||||
|
||||
#. pylint: disable=W0702
|
||||
#: ../nova/compute/manager.py:219
|
||||
|
@ -314,7 +316,7 @@ msgstr ""
|
|||
#: ../nova/compute/manager.py:233 ../nova/tests/test_cloud.py:286
|
||||
#, python-format
|
||||
msgid "Terminating instance %s"
|
||||
msgstr ""
|
||||
msgstr "Beende Instanz %s"
|
||||
|
||||
#: ../nova/compute/manager.py:255
|
||||
#, python-format
|
||||
|
@ -377,7 +379,7 @@ msgstr ""
|
|||
#: ../nova/compute/manager.py:372
|
||||
#, python-format
|
||||
msgid "instance %s: rescuing"
|
||||
msgstr ""
|
||||
msgstr "Instanz %s: Rettung"
|
||||
|
||||
#: ../nova/compute/manager.py:387
|
||||
#, python-format
|
||||
|
@ -387,12 +389,12 @@ msgstr ""
|
|||
#: ../nova/compute/manager.py:406
|
||||
#, python-format
|
||||
msgid "instance %s: pausing"
|
||||
msgstr ""
|
||||
msgstr "Instanz %s pausiert"
|
||||
|
||||
#: ../nova/compute/manager.py:423
|
||||
#, python-format
|
||||
msgid "instance %s: unpausing"
|
||||
msgstr ""
|
||||
msgstr "Instanz %s wird fortgesetzt"
|
||||
|
||||
#: ../nova/compute/manager.py:440
|
||||
#, python-format
|
||||
|
@ -584,7 +586,7 @@ msgstr ""
|
|||
|
||||
#: ../nova/virt/connection.py:73
|
||||
msgid "Failed to open connection to the hypervisor"
|
||||
msgstr ""
|
||||
msgstr "Konnte Verbindung zum Hypervisor nicht öffnen"
|
||||
|
||||
#: ../nova/network/linux_net.py:187
|
||||
#, python-format
|
||||
|
@ -637,7 +639,7 @@ msgstr "Klasse %s konnte nicht gefunden werden"
|
|||
#: ../nova/utils.py:118
|
||||
#, python-format
|
||||
msgid "Fetching %s"
|
||||
msgstr ""
|
||||
msgstr "Hole %s"
|
||||
|
||||
#: ../nova/utils.py:130
|
||||
#, python-format
|
||||
|
@ -2562,7 +2564,7 @@ msgstr ""
|
|||
#: ../nova/auth/manager.py:270
|
||||
#, python-format
|
||||
msgid "Using project name = user name (%s)"
|
||||
msgstr ""
|
||||
msgstr "Verwende Project-Name = User-Name (%s)"
|
||||
|
||||
#: ../nova/auth/manager.py:277
|
||||
#, python-format
|
||||
|
@ -2572,7 +2574,7 @@ msgstr ""
|
|||
#: ../nova/auth/manager.py:279
|
||||
#, python-format
|
||||
msgid "No project called %s could be found"
|
||||
msgstr ""
|
||||
msgstr "Es konnte kein Projekt mit dem Namen %s gefunden werden"
|
||||
|
||||
#: ../nova/auth/manager.py:287
|
||||
#, python-format
|
||||
|
@ -2696,6 +2698,7 @@ msgstr ""
|
|||
#: ../nova/service.py:195
|
||||
msgid "The service database object disappeared, Recreating it."
|
||||
msgstr ""
|
||||
"Das Service-Datenbank-Objekt ist verschwunden, es wird erneut erzeugt."
|
||||
|
||||
#: ../nova/service.py:207
|
||||
msgid "Recovered model server connection!"
|
||||
|
@ -2723,7 +2726,7 @@ msgstr ""
|
|||
#: ../nova/auth/ldapdriver.py:472
|
||||
#, python-format
|
||||
msgid "Group can't be created because group %s already exists"
|
||||
msgstr ""
|
||||
msgstr "Die Gruppe %s kann nicht angelegt werde, da sie bereits existiert"
|
||||
|
||||
#: ../nova/auth/ldapdriver.py:478
|
||||
#, python-format
|
||||
|
@ -2739,6 +2742,7 @@ msgstr ""
|
|||
#, python-format
|
||||
msgid "User %s can't be added to the group because the user doesn't exist"
|
||||
msgstr ""
|
||||
"Der User %s kann nicht zur Gruppe hinzugefügt werde, da er nicht existiert"
|
||||
|
||||
#: ../nova/auth/ldapdriver.py:510 ../nova/auth/ldapdriver.py:521
|
||||
#, python-format
|
||||
|
@ -2755,6 +2759,7 @@ msgstr ""
|
|||
msgid ""
|
||||
"User %s can't be removed from the group because the user doesn't exist"
|
||||
msgstr ""
|
||||
"Der User %s kann nicht aus der Gruppe entfernt werden, da er nicht existiert"
|
||||
|
||||
#: ../nova/auth/ldapdriver.py:528
|
||||
#, python-format
|
||||
|
@ -2840,7 +2845,7 @@ msgstr ""
|
|||
#: ../nova/api/ec2/admin.py:200
|
||||
#, python-format
|
||||
msgid "Delete project: %s"
|
||||
msgstr ""
|
||||
msgstr "Lösche Projekt %s"
|
||||
|
||||
#: ../nova/api/ec2/admin.py:214
|
||||
#, python-format
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
139
po/es.po
139
po/es.po
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: nova\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-02-21 10:03-0500\n"
|
||||
"PO-Revision-Date: 2011-03-17 15:54+0000\n"
|
||||
"Last-Translator: Erick Huezo <erickhuezo@gmail.com>\n"
|
||||
"PO-Revision-Date: 2011-06-30 16:42+0000\n"
|
||||
"Last-Translator: David Caro <Unknown>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:19+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
@ -36,10 +36,15 @@ msgid ""
|
|||
"Stdout: %(stdout)r\n"
|
||||
"Stderr: %(stderr)r"
|
||||
msgstr ""
|
||||
"%(description)s\n"
|
||||
"Comando: %(cmd)s\n"
|
||||
"Código de salida: %(exit_code)s\n"
|
||||
"Stdout: %(stdout)r\n"
|
||||
"Stderr: %(stderr)r"
|
||||
|
||||
#: ../nova/exception.py:107
|
||||
msgid "DB exception wrapped"
|
||||
msgstr ""
|
||||
msgstr "Excepción DB encapsulada"
|
||||
|
||||
#. exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
#: ../nova/exception.py:120
|
||||
|
@ -49,12 +54,12 @@ msgstr "Excepción no controlada"
|
|||
#: ../nova/volume/api.py:45
|
||||
#, python-format
|
||||
msgid "Quota exceeeded for %(pid)s, tried to create %(size)sG volume"
|
||||
msgstr ""
|
||||
msgstr "Cuota excedida por %(pid)s, se intentó crear el volumen %(size)sG"
|
||||
|
||||
#: ../nova/volume/api.py:47
|
||||
#, python-format
|
||||
msgid "Volume quota exceeded. You cannot create a volume of size %sG"
|
||||
msgstr "Cuota excedida. No puedes crear un volumen con tamaño %sG"
|
||||
msgstr "Cuota excedida. No puede crear un volumen con tamaño %sG"
|
||||
|
||||
#: ../nova/volume/api.py:71 ../nova/volume/api.py:96
|
||||
msgid "Volume status must be available"
|
||||
|
@ -83,7 +88,7 @@ msgstr "%(param)s propiedad no encontrada para la imagen %(_image_id)s"
|
|||
|
||||
#: ../nova/api/openstack/servers.py:168
|
||||
msgid "No keypairs defined"
|
||||
msgstr "No se definio una Keypairs"
|
||||
msgstr "No se definio un par de llaves (Keypair)"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:238
|
||||
#, python-format
|
||||
|
@ -103,7 +108,7 @@ msgstr "Compute.api::get_lock %s"
|
|||
#: ../nova/api/openstack/servers.py:281
|
||||
#, python-format
|
||||
msgid "Compute.api::reset_network %s"
|
||||
msgstr ""
|
||||
msgstr "Compute.api::reset_network %s"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:292
|
||||
#, python-format
|
||||
|
@ -127,16 +132,16 @@ msgstr "compute.api::resume %s"
|
|||
|
||||
#: ../nova/twistd.py:157
|
||||
msgid "Wrong number of arguments."
|
||||
msgstr "Numero de argumentos incorrectos"
|
||||
msgstr "Cantidad de argumentos incorrecta"
|
||||
|
||||
#: ../nova/twistd.py:209
|
||||
#, python-format
|
||||
msgid "pidfile %s does not exist. Daemon not running?\n"
|
||||
msgstr "el pidfile %s no existe. ¿No estará el demonio parado?\n"
|
||||
msgstr "El \"pidfile\" %s no existe. Quizás el servicio no este corriendo.\n"
|
||||
|
||||
#: ../nova/twistd.py:221
|
||||
msgid "No such process"
|
||||
msgstr "No se encontró proceso"
|
||||
msgstr "No existe el proceso"
|
||||
|
||||
#: ../nova/twistd.py:230 ../nova/service.py:224
|
||||
#, python-format
|
||||
|
@ -145,12 +150,12 @@ msgstr "Sirviendo %s"
|
|||
|
||||
#: ../nova/twistd.py:262 ../nova/service.py:225
|
||||
msgid "Full set of FLAGS:"
|
||||
msgstr "Conjunto completo de opciones:"
|
||||
msgstr "Conjunto completo de opciones (FLAGS):"
|
||||
|
||||
#: ../nova/twistd.py:266
|
||||
#, python-format
|
||||
msgid "Starting %s"
|
||||
msgstr "Comenzando %s"
|
||||
msgstr "Iniciando %s"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:48 ../nova/virt/xenapi/volumeops.py:101
|
||||
#: ../nova/db/sqlalchemy/api.py:731 ../nova/virt/libvirt_conn.py:741
|
||||
|
@ -163,17 +168,19 @@ msgstr "La instancia %s no se ha encontrado"
|
|||
#: ../nova/virt/xenapi/volumeops.py:51
|
||||
#, python-format
|
||||
msgid "Attach_volume: %(instance_name)s, %(device_path)s, %(mountpoint)s"
|
||||
msgstr ""
|
||||
msgstr "Volumen_unido: %(instance_name)s, %(device_path)s, %(mountpoint)s"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:69
|
||||
#, python-format
|
||||
msgid "Unable to create VDI on SR %(sr_ref)s for instance %(instance_name)s"
|
||||
msgstr ""
|
||||
"No es posible crear el VDI en SR %(sr_ref)s para la instancia "
|
||||
"%(instance_name)s"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:80
|
||||
#, python-format
|
||||
msgid "Unable to use SR %(sr_ref)s for instance %(instance_name)s"
|
||||
msgstr ""
|
||||
msgstr "No es posible usar SR %(sr_ref)s para la instancia %(instance_name)s"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:91
|
||||
#, python-format
|
||||
|
@ -184,12 +191,14 @@ msgstr "Imposible adjuntar volumen a la instancia %s"
|
|||
#, python-format
|
||||
msgid "Mountpoint %(mountpoint)s attached to instance %(instance_name)s"
|
||||
msgstr ""
|
||||
"El punto de montaje %(mountpoint)s esta unido a la instancia "
|
||||
"%(instance_name)s"
|
||||
|
||||
#. Detach VBD from VM
|
||||
#: ../nova/virt/xenapi/volumeops.py:104
|
||||
#, python-format
|
||||
msgid "Detach_volume: %(instance_name)s, %(mountpoint)s"
|
||||
msgstr ""
|
||||
msgstr "Volume_separado: %(instance_name)s, %(mountpoint)s"
|
||||
|
||||
#: ../nova/virt/xenapi/volumeops.py:112
|
||||
#, python-format
|
||||
|
@ -205,6 +214,8 @@ msgstr "Imposible desasociar volumen %s"
|
|||
#, python-format
|
||||
msgid "Mountpoint %(mountpoint)s detached from instance %(instance_name)s"
|
||||
msgstr ""
|
||||
"El punto de montaje %(mountpoint)s se desligó de la instancia "
|
||||
"%(instance_name)s"
|
||||
|
||||
#: ../nova/compute/instance_types.py:41
|
||||
#, python-format
|
||||
|
@ -259,7 +270,7 @@ msgstr ""
|
|||
#: ../nova/crypto.py:258
|
||||
#, python-format
|
||||
msgid "Flags path: %s"
|
||||
msgstr ""
|
||||
msgstr "Ruta a las opciones: %s"
|
||||
|
||||
#: ../nova/scheduler/manager.py:69
|
||||
#, python-format
|
||||
|
@ -276,6 +287,7 @@ msgstr "check_instance_lock: decorating: |%s|"
|
|||
msgid ""
|
||||
"check_instance_lock: arguments: |%(self)s| |%(context)s| |%(instance_id)s|"
|
||||
msgstr ""
|
||||
"check_instance_lock: argumentos: |%(self)s| |%(context)s| |%(instance_id)s|"
|
||||
|
||||
#: ../nova/compute/manager.py:84
|
||||
#, python-format
|
||||
|
@ -338,6 +350,8 @@ msgid ""
|
|||
"trying to reboot a non-running instance: %(instance_id)s (state: %(state)s "
|
||||
"expected: %(running)s)"
|
||||
msgstr ""
|
||||
"intentando reiniciar una instancia no ejecutada: %(instance_id)s (state: "
|
||||
"%(state)s expected: %(running)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:311
|
||||
#, python-format
|
||||
|
@ -350,6 +364,8 @@ msgid ""
|
|||
"trying to snapshot a non-running instance: %(instance_id)s (state: %(state)s "
|
||||
"expected: %(running)s)"
|
||||
msgstr ""
|
||||
"intentando crear una imagen instantanea(snapshot) de una maquina no "
|
||||
"ejecutada: %(instance_id)s (state: %(state)s expected: %(running)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:332
|
||||
#, python-format
|
||||
|
@ -357,11 +373,13 @@ msgid ""
|
|||
"trying to reset the password on a non-running instance: %(instance_id)s "
|
||||
"(state: %(instance_state)s expected: %(expected_state)s)"
|
||||
msgstr ""
|
||||
"intentando restablecer el password en una instancia: %(instance_id)s "
|
||||
"(estado: %(instance_state)s esperado: %(expected_state)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:335
|
||||
#, python-format
|
||||
msgid "instance %s: setting admin password"
|
||||
msgstr ""
|
||||
msgstr "instancia %s: estableciendo password de administrador"
|
||||
|
||||
#: ../nova/compute/manager.py:353
|
||||
#, python-format
|
||||
|
@ -369,11 +387,13 @@ msgid ""
|
|||
"trying to inject a file into a non-running instance: %(instance_id)s (state: "
|
||||
"%(instance_state)s expected: %(expected_state)s)"
|
||||
msgstr ""
|
||||
"intentando inyectar un archivo dentro de una instancia parada: "
|
||||
"%(instance_id)s (estado: %(instance_state)s esperado: %(expected_state)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:362
|
||||
#, python-format
|
||||
msgid "instance %(nm)s: injecting file to %(plain_path)s"
|
||||
msgstr ""
|
||||
msgstr "instancia %(nm)s: inyectando archivo en %(plain_path)s"
|
||||
|
||||
#: ../nova/compute/manager.py:372
|
||||
#, python-format
|
||||
|
@ -393,7 +413,7 @@ msgstr "instancia %s: pausando"
|
|||
#: ../nova/compute/manager.py:423
|
||||
#, python-format
|
||||
msgid "instance %s: unpausing"
|
||||
msgstr "instnacia %s: continuando tras pausa"
|
||||
msgstr "instancia %s: continuando tras pausa"
|
||||
|
||||
#: ../nova/compute/manager.py:440
|
||||
#, python-format
|
||||
|
@ -403,7 +423,7 @@ msgstr "instancia %s: obteniendo los diagnosticos"
|
|||
#: ../nova/compute/manager.py:453
|
||||
#, python-format
|
||||
msgid "instance %s: suspending"
|
||||
msgstr ""
|
||||
msgstr "instancia %s: suspendiendo"
|
||||
|
||||
#: ../nova/compute/manager.py:472
|
||||
#, python-format
|
||||
|
@ -501,7 +521,7 @@ msgstr "Exportando de nuevo los volumenes %s"
|
|||
#: ../nova/volume/manager.py:90
|
||||
#, python-format
|
||||
msgid "volume %s: skipping export"
|
||||
msgstr ""
|
||||
msgstr "volume %s: saltando exportación"
|
||||
|
||||
#: ../nova/volume/manager.py:96
|
||||
#, python-format
|
||||
|
@ -511,7 +531,7 @@ msgstr "volumen %s: creando"
|
|||
#: ../nova/volume/manager.py:108
|
||||
#, python-format
|
||||
msgid "volume %(vol_name)s: creating lv of size %(vol_size)sG"
|
||||
msgstr ""
|
||||
msgstr "volume %(vol_name)s: creando lv del tamaño %(vol_size)sG"
|
||||
|
||||
#: ../nova/volume/manager.py:112
|
||||
#, python-format
|
||||
|
@ -549,7 +569,7 @@ msgstr "volumen %s: eliminado satisfactoriamente"
|
|||
#: ../nova/virt/xenapi/fake.py:74
|
||||
#, python-format
|
||||
msgid "%(text)s: _db_content => %(content)s"
|
||||
msgstr ""
|
||||
msgstr "%(text)s: _db_content => %(content)s"
|
||||
|
||||
#: ../nova/virt/xenapi/fake.py:304 ../nova/virt/xenapi/fake.py:404
|
||||
#: ../nova/virt/xenapi/fake.py:422 ../nova/virt/xenapi/fake.py:478
|
||||
|
@ -564,7 +584,7 @@ msgstr "xenapi.fake no tiene una implementación para %s"
|
|||
#: ../nova/virt/xenapi/fake.py:341
|
||||
#, python-format
|
||||
msgid "Calling %(localname)s %(impl)s"
|
||||
msgstr ""
|
||||
msgstr "Llamando %(localname)s %(impl)s"
|
||||
|
||||
#: ../nova/virt/xenapi/fake.py:346
|
||||
#, python-format
|
||||
|
@ -618,12 +638,12 @@ msgstr "El pid %d está pasado, relanzando dnsmasq"
|
|||
#: ../nova/network/linux_net.py:358
|
||||
#, python-format
|
||||
msgid "killing radvd threw %s"
|
||||
msgstr ""
|
||||
msgstr "Matando radvd lanzado %s"
|
||||
|
||||
#: ../nova/network/linux_net.py:360
|
||||
#, python-format
|
||||
msgid "Pid %d is stale, relaunching radvd"
|
||||
msgstr ""
|
||||
msgstr "Pid %d corrupto, relanzando radvd"
|
||||
|
||||
#. pylint: disable=W0703
|
||||
#: ../nova/network/linux_net.py:449
|
||||
|
@ -659,7 +679,7 @@ msgstr "El resultado fue %s"
|
|||
#: ../nova/utils.py:159
|
||||
#, python-format
|
||||
msgid "Running cmd (SSH): %s"
|
||||
msgstr ""
|
||||
msgstr "corriendo cmd (SSH): %s"
|
||||
|
||||
#: ../nova/utils.py:217
|
||||
#, python-format
|
||||
|
@ -674,12 +694,12 @@ msgstr "Ejecutando %s"
|
|||
#: ../nova/utils.py:262
|
||||
#, python-format
|
||||
msgid "Link Local address is not found.:%s"
|
||||
msgstr ""
|
||||
msgstr "No se encuentra la dirección del enlace local.:%s"
|
||||
|
||||
#: ../nova/utils.py:265
|
||||
#, python-format
|
||||
msgid "Couldn't get Link Local IP of %(interface)s :%(ex)s"
|
||||
msgstr ""
|
||||
msgstr "No se pudo obtener enlace de la ip local de %(interface)s :%(ex)s"
|
||||
|
||||
#: ../nova/utils.py:363
|
||||
#, python-format
|
||||
|
@ -694,7 +714,7 @@ msgstr "backend %s"
|
|||
#: ../nova/fakerabbit.py:49
|
||||
#, python-format
|
||||
msgid "(%(nm)s) publish (key: %(routing_key)s) %(message)s"
|
||||
msgstr ""
|
||||
msgstr "(%(nm)s) publica (key: %(routing_key)s) %(message)s"
|
||||
|
||||
#: ../nova/fakerabbit.py:54
|
||||
#, python-format
|
||||
|
@ -714,12 +734,12 @@ msgstr "Declarando intercambio %s"
|
|||
#: ../nova/fakerabbit.py:96
|
||||
#, python-format
|
||||
msgid "Binding %(queue)s to %(exchange)s with key %(routing_key)s"
|
||||
msgstr ""
|
||||
msgstr "Enlazando %(queue)s a %(exchange)s con la llave %(routing_key)s"
|
||||
|
||||
#: ../nova/fakerabbit.py:121
|
||||
#, python-format
|
||||
msgid "Getting from %(queue)s: %(message)s"
|
||||
msgstr ""
|
||||
msgstr "Obtendiendo desde %(queue)s: %(message)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:135 ../nova/virt/hyperv.py:171
|
||||
#, python-format
|
||||
|
@ -729,17 +749,17 @@ msgstr "Creada VM %s..."
|
|||
#: ../nova/virt/xenapi/vm_utils.py:138
|
||||
#, python-format
|
||||
msgid "Created VM %(instance_name)s as %(vm_ref)s."
|
||||
msgstr ""
|
||||
msgstr "VM creada %(instance_name)s como %(vm_ref)s."
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:168
|
||||
#, python-format
|
||||
msgid "Creating VBD for VM %(vm_ref)s, VDI %(vdi_ref)s ... "
|
||||
msgstr ""
|
||||
msgstr "Creando VBD para VM %(vm_ref)s, VDI %(vdi_ref)s ... "
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:171
|
||||
#, python-format
|
||||
msgid "Created VBD %(vbd_ref)s for VM %(vm_ref)s, VDI %(vdi_ref)s."
|
||||
msgstr ""
|
||||
msgstr "Creado el VBD %(vbd_ref)s para VM %(vm_ref)s, VDI %(vdi_ref)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:187
|
||||
#, python-format
|
||||
|
@ -759,12 +779,12 @@ msgstr "Imposible destruir VBD %s"
|
|||
#: ../nova/virt/xenapi/vm_utils.py:224
|
||||
#, python-format
|
||||
msgid "Creating VIF for VM %(vm_ref)s, network %(network_ref)s."
|
||||
msgstr ""
|
||||
msgstr "Creando VIF para VM %(vm_ref)s, red %(network_ref)s."
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:227
|
||||
#, python-format
|
||||
msgid "Created VIF %(vif_ref)s for VM %(vm_ref)s, network %(network_ref)s."
|
||||
msgstr ""
|
||||
msgstr "Creado el VIF %(vif_ref)s para VM %(vm_ref)s, red %(network_ref)s."
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:246
|
||||
#, python-format
|
||||
|
@ -772,50 +792,52 @@ msgid ""
|
|||
"Created VDI %(vdi_ref)s (%(name_label)s, %(virtual_size)s, %(read_only)s) on "
|
||||
"%(sr_ref)s."
|
||||
msgstr ""
|
||||
"VDI creado %(vdi_ref)s (%(name_label)s, %(virtual_size)s, %(read_only)s) "
|
||||
"sobre %(sr_ref)s."
|
||||
|
||||
#. TODO(sirp): Add quiesce and VSS locking support when Windows support
|
||||
#. is added
|
||||
#: ../nova/virt/xenapi/vm_utils.py:258
|
||||
#, python-format
|
||||
msgid "Snapshotting VM %(vm_ref)s with label '%(label)s'..."
|
||||
msgstr ""
|
||||
msgstr "Creando snapshot de la VM %(vm_ref)s con etiqueta '%(label)s'..."
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:272
|
||||
#, python-format
|
||||
msgid "Created snapshot %(template_vm_ref)s from VM %(vm_ref)s."
|
||||
msgstr ""
|
||||
msgstr "Instantánea creada %(template_vm_ref)s de la VM %(vm_ref)s."
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:286
|
||||
#, python-format
|
||||
msgid "Asking xapi to upload %(vdi_uuids)s as ID %(image_id)s"
|
||||
msgstr ""
|
||||
msgstr "Pidiendo xapi a subir %(vdi_uuids)s como ID %(image_id)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:327
|
||||
#, python-format
|
||||
msgid "Size for image %(image)s:%(virtual_size)d"
|
||||
msgstr ""
|
||||
msgstr "Tamaño para imagen %(image)s:%(virtual_size)d"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:332
|
||||
#, python-format
|
||||
msgid "Glance image %s"
|
||||
msgstr ""
|
||||
msgstr "Imagen Glance %s"
|
||||
|
||||
#. we need to invoke a plugin for copying VDI's
|
||||
#. content into proper path
|
||||
#: ../nova/virt/xenapi/vm_utils.py:342
|
||||
#, python-format
|
||||
msgid "Copying VDI %s to /boot/guest on dom0"
|
||||
msgstr ""
|
||||
msgstr "Copiando VDI %s a /boot/guest on dom0"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:352
|
||||
#, python-format
|
||||
msgid "Kernel/Ramdisk VDI %s destroyed"
|
||||
msgstr ""
|
||||
msgstr "Kernel/Ramdisk VDI %s destruído"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:361
|
||||
#, python-format
|
||||
msgid "Asking xapi to fetch %(url)s as %(access)s"
|
||||
msgstr ""
|
||||
msgstr "Pidiendo a xapi que descargue %(url)s como %(access)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:386 ../nova/virt/xenapi/vm_utils.py:402
|
||||
#, python-format
|
||||
|
@ -825,21 +847,21 @@ msgstr "Buscando vid %s para el kernel PV"
|
|||
#: ../nova/virt/xenapi/vm_utils.py:397
|
||||
#, python-format
|
||||
msgid "PV Kernel in VDI:%s"
|
||||
msgstr ""
|
||||
msgstr "Kernel PV en VDI:%s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:405
|
||||
#, python-format
|
||||
msgid "Running pygrub against %s"
|
||||
msgstr ""
|
||||
msgstr "Ejecutando pygrub contra %s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:411
|
||||
#, python-format
|
||||
msgid "Found Xen kernel %s"
|
||||
msgstr ""
|
||||
msgstr "Kernel Xen Encontrado %s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:413
|
||||
msgid "No Xen kernel found. Booting HVM."
|
||||
msgstr ""
|
||||
msgstr "Kernel Xen no encontrado. Reiniciando HVM"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:425 ../nova/virt/hyperv.py:431
|
||||
#, python-format
|
||||
|
@ -864,7 +886,7 @@ msgstr "(VM_UTILS) xenapi power_state -> |%s|"
|
|||
#: ../nova/virt/xenapi/vm_utils.py:525
|
||||
#, python-format
|
||||
msgid "VHD %(vdi_uuid)s has parent %(parent_ref)s"
|
||||
msgstr ""
|
||||
msgstr "VHD %(vdi_uuid)s tiene origen en %(parent_ref)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:542
|
||||
#, python-format
|
||||
|
@ -893,18 +915,19 @@ msgstr "No se han encontrado VDI's para VM %s"
|
|||
#, python-format
|
||||
msgid "Unexpected number of VDIs (%(num_vdis)s) found for VM %(vm_ref)s"
|
||||
msgstr ""
|
||||
"Numero de VDIs inesperado (%(num_vdis)s) encontrados por VM %(vm_ref)s"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:653
|
||||
#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:188
|
||||
#, python-format
|
||||
msgid "Creating VBD for VDI %s ... "
|
||||
msgstr ""
|
||||
msgstr "Creando VBD para VDI %s ... "
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:655
|
||||
#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:190
|
||||
#, python-format
|
||||
msgid "Creating VBD for VDI %s done."
|
||||
msgstr ""
|
||||
msgstr "Creando VBF para VDI %s terminado"
|
||||
|
||||
#: ../nova/virt/xenapi/vm_utils.py:657
|
||||
#: ../plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py:192
|
||||
|
@ -2850,12 +2873,12 @@ msgstr ""
|
|||
#: ../nova/api/ec2/admin.py:177
|
||||
#, python-format
|
||||
msgid "Create project %(name)s managed by %(manager_user)s"
|
||||
msgstr ""
|
||||
msgstr "Crear proyecto %(name)s administrador por %(manager_user)s"
|
||||
|
||||
#: ../nova/api/ec2/admin.py:190
|
||||
#, python-format
|
||||
msgid "Modify project: %(name)s managed by %(manager_user)s"
|
||||
msgstr ""
|
||||
msgstr "Modificar proyecto: %(name)s administrado por %(manager_user)s"
|
||||
|
||||
#: ../nova/api/ec2/admin.py:200
|
||||
#, python-format
|
||||
|
@ -2865,12 +2888,12 @@ msgstr "Borrar proyecto: %s"
|
|||
#: ../nova/api/ec2/admin.py:214
|
||||
#, python-format
|
||||
msgid "Adding user %(user)s to project %(project)s"
|
||||
msgstr ""
|
||||
msgstr "Agregando usuario %(user)s al proyecto %(project)s"
|
||||
|
||||
#: ../nova/api/ec2/admin.py:218
|
||||
#, python-format
|
||||
msgid "Removing user %(user)s from project %(project)s"
|
||||
msgstr ""
|
||||
msgstr "Eliminando el usuario %(user)s del proyecto %(project)s"
|
||||
|
||||
#, python-format
|
||||
#~ msgid ""
|
||||
|
|
4
po/it.po
4
po/it.po
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:19+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-25 05:22+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
22
po/ru.po
22
po/ru.po
|
@ -8,20 +8,20 @@ msgstr ""
|
|||
"Project-Id-Version: nova\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-02-21 10:03-0500\n"
|
||||
"PO-Revision-Date: 2011-03-30 07:06+0000\n"
|
||||
"Last-Translator: Andrey Olykainen <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-07-09 07:20+0000\n"
|
||||
"Last-Translator: ilya kislicyn <Unknown>\n"
|
||||
"Language-Team: Russian <ru@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-31 05:58+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
#: ../nova/scheduler/simple.py:122
|
||||
msgid "No hosts found"
|
||||
msgstr ""
|
||||
msgstr "Узлы не найдены"
|
||||
|
||||
#: ../nova/exception.py:33
|
||||
msgid "Unexpected error while running command."
|
||||
|
@ -54,7 +54,7 @@ msgstr ""
|
|||
#: ../nova/volume/api.py:47
|
||||
#, python-format
|
||||
msgid "Volume quota exceeded. You cannot create a volume of size %sG"
|
||||
msgstr ""
|
||||
msgstr "Квота тома превышена. Вы не можете создать том размером %sG"
|
||||
|
||||
#: ../nova/volume/api.py:71 ../nova/volume/api.py:96
|
||||
msgid "Volume status must be available"
|
||||
|
@ -62,19 +62,19 @@ msgstr ""
|
|||
|
||||
#: ../nova/volume/api.py:98
|
||||
msgid "Volume is already attached"
|
||||
msgstr ""
|
||||
msgstr "Том уже смотирован"
|
||||
|
||||
#: ../nova/volume/api.py:104
|
||||
msgid "Volume is already detached"
|
||||
msgstr ""
|
||||
msgstr "Том уже отмонтирован"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:72
|
||||
msgid "Failed to read private ip"
|
||||
msgstr ""
|
||||
msgstr "Ошибка чтения приватного IP адреса"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:79
|
||||
msgid "Failed to read public ip(s)"
|
||||
msgstr ""
|
||||
msgstr "Ошибка чтения публичных IP адресов"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:152
|
||||
#, python-format
|
||||
|
@ -83,7 +83,7 @@ msgstr ""
|
|||
|
||||
#: ../nova/api/openstack/servers.py:168
|
||||
msgid "No keypairs defined"
|
||||
msgstr ""
|
||||
msgstr "Не определены ключевые пары"
|
||||
|
||||
#: ../nova/api/openstack/servers.py:238
|
||||
#, python-format
|
||||
|
|
4
po/uk.po
4
po/uk.po
|
@ -14,8 +14,8 @@ msgstr ""
|
|||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-19 06:19+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/scheduler/chance.py:37 ../nova/scheduler/zone.py:55
|
||||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
|
|
27
po/zh_CN.po
27
po/zh_CN.po
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: nova\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-02-21 10:03-0500\n"
|
||||
"PO-Revision-Date: 2011-04-07 05:01+0000\n"
|
||||
"Last-Translator: ben <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-06-14 14:44+0000\n"
|
||||
"Last-Translator: chong <Unknown>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-04-08 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 12735)\n"
|
||||
"X-Launchpad-Export-Date: 2011-07-23 05:12+0000\n"
|
||||
"X-Generator: Launchpad (build 13405)\n"
|
||||
|
||||
#: ../nova/twistd.py:266
|
||||
#, python-format
|
||||
|
@ -26,7 +26,7 @@ msgstr "启动 %s 中"
|
|||
#: ../nova/scheduler/simple.py:75 ../nova/scheduler/simple.py:110
|
||||
#: ../nova/scheduler/simple.py:122
|
||||
msgid "No hosts found"
|
||||
msgstr "未找到主机"
|
||||
msgstr "没有找到主机"
|
||||
|
||||
#: ../nova/exception.py:33
|
||||
msgid "Unexpected error while running command."
|
||||
|
@ -41,6 +41,11 @@ msgid ""
|
|||
"Stdout: %(stdout)r\n"
|
||||
"Stderr: %(stderr)r"
|
||||
msgstr ""
|
||||
"%(description)s\n"
|
||||
"命令: %(cmd)s\n"
|
||||
"退出代码: %(exit_code)s\n"
|
||||
"标准输出: %(stdout)r\n"
|
||||
"标准出错: %(stderr)r"
|
||||
|
||||
#: ../nova/exception.py:107
|
||||
msgid "DB exception wrapped"
|
||||
|
@ -309,17 +314,17 @@ msgstr ""
|
|||
#: ../nova/compute/manager.py:233 ../nova/tests/test_cloud.py:286
|
||||
#, python-format
|
||||
msgid "Terminating instance %s"
|
||||
msgstr ""
|
||||
msgstr "正在结束实例 %s"
|
||||
|
||||
#: ../nova/compute/manager.py:255
|
||||
#, python-format
|
||||
msgid "Deallocating address %s"
|
||||
msgstr ""
|
||||
msgstr "取消分配地址 %s"
|
||||
|
||||
#: ../nova/compute/manager.py:268
|
||||
#, python-format
|
||||
msgid "trying to destroy already destroyed instance: %s"
|
||||
msgstr ""
|
||||
msgstr "尝试销毁已经销毁的实例: %s"
|
||||
|
||||
#: ../nova/compute/manager.py:282
|
||||
#, python-format
|
||||
|
@ -331,12 +336,12 @@ msgstr "重启虚拟机 %s"
|
|||
msgid ""
|
||||
"trying to reboot a non-running instance: %(instance_id)s (state: %(state)s "
|
||||
"expected: %(running)s)"
|
||||
msgstr ""
|
||||
msgstr "尝试重启没有在运行中实例: %(instance_id)s (状态: %(state)s 预料: %(running)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:311
|
||||
#, python-format
|
||||
msgid "instance %s: snapshotting"
|
||||
msgstr ""
|
||||
msgstr "实例 %s: 快照中"
|
||||
|
||||
#: ../nova/compute/manager.py:316
|
||||
#, python-format
|
||||
|
@ -351,6 +356,8 @@ msgid ""
|
|||
"trying to reset the password on a non-running instance: %(instance_id)s "
|
||||
"(state: %(instance_state)s expected: %(expected_state)s)"
|
||||
msgstr ""
|
||||
"尝试对没有在运行的实例重置密码: %(instance_id)s (状态: %(instance_state)s 预料: "
|
||||
"%(expected_state)s)"
|
||||
|
||||
#: ../nova/compute/manager.py:335
|
||||
#, python-format
|
||||
|
|
File diff suppressed because it is too large
Load Diff
18
run_tests.sh
18
run_tests.sh
|
@ -11,6 +11,7 @@ function usage {
|
|||
echo " -x, --stop Stop running tests after the first error or failure."
|
||||
echo " -f, --force Force a clean re-build of the virtual environment. Useful when dependencies have been added."
|
||||
echo " -p, --pep8 Just run pep8"
|
||||
echo " -c, --coverage Generate coverage report"
|
||||
echo " -h, --help Print this usage message"
|
||||
echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list"
|
||||
echo ""
|
||||
|
@ -29,6 +30,7 @@ function process_option {
|
|||
-n|--no-recreate-db) let recreate_db=0;;
|
||||
-f|--force) let force=1;;
|
||||
-p|--pep8) let just_pep8=1;;
|
||||
-c|--coverage) let coverage=1;;
|
||||
-*) noseopts="$noseopts $1";;
|
||||
*) noseargs="$noseargs $1"
|
||||
esac
|
||||
|
@ -43,12 +45,18 @@ noseargs=
|
|||
noseopts=
|
||||
wrapper=""
|
||||
just_pep8=0
|
||||
coverage=0
|
||||
recreate_db=1
|
||||
|
||||
for arg in "$@"; do
|
||||
process_option $arg
|
||||
done
|
||||
|
||||
# If enabled, tell nose to collect coverage data
|
||||
if [ $coverage -eq 1 ]; then
|
||||
noseopts="$noseopts --with-coverage --cover-package=nova"
|
||||
fi
|
||||
|
||||
function run_tests {
|
||||
# Just run the test suites in current environment
|
||||
${wrapper} $NOSETESTS 2> run_tests.log
|
||||
|
@ -108,6 +116,11 @@ then
|
|||
fi
|
||||
fi
|
||||
|
||||
# Delete old coverage data from previous runs
|
||||
if [ $coverage -eq 1 ]; then
|
||||
${wrapper} coverage erase
|
||||
fi
|
||||
|
||||
if [ $just_pep8 -eq 1 ]; then
|
||||
run_pep8
|
||||
exit
|
||||
|
@ -126,3 +139,8 @@ run_tests || exit
|
|||
if [ -z "$noseargs" ]; then
|
||||
run_pep8
|
||||
fi
|
||||
|
||||
if [ $coverage -eq 1 ]; then
|
||||
echo "Generating coverage report in covhtml/"
|
||||
${wrapper} coverage html -d covhtml -i
|
||||
fi
|
||||
|
|
|
@ -21,6 +21,7 @@ On Windows we require pyWin32 installed on Python.
|
|||
"""
|
||||
|
||||
import array
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
|
@ -30,6 +31,8 @@ import subprocess
|
|||
import sys
|
||||
import time
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
PLATFORM_WIN = 'win32'
|
||||
PLATFORM_LINUX = 'linux2'
|
||||
ARCH_32_BIT = '32bit'
|
||||
|
@ -275,7 +278,8 @@ def _filter_duplicates(all_entries):
|
|||
return final_list
|
||||
|
||||
|
||||
def _set_rhel_networking(network_details=[]):
|
||||
def _set_rhel_networking(network_details=None):
|
||||
network_details = network_details or []
|
||||
all_dns_servers = []
|
||||
for network_detail in network_details:
|
||||
mac_address, ip_address, subnet_mask, gateway, broadcast,\
|
||||
|
@ -315,6 +319,46 @@ def _set_rhel_networking(network_details=[]):
|
|||
_execute(['/sbin/service', 'network', 'restart'])
|
||||
|
||||
|
||||
def _set_ubuntu_networking(network_details=None):
|
||||
network_details = network_details or []
|
||||
""" Set IPv4 network settings for Ubuntu """
|
||||
all_dns_servers = []
|
||||
for network_detail in network_details:
|
||||
mac_address, ip_address, subnet_mask, gateway, broadcast,\
|
||||
dns_servers = network_detail
|
||||
all_dns_servers.extend(dns_servers)
|
||||
adapter_name, current_ip_address = \
|
||||
_get_linux_adapter_name_and_ip_address(mac_address)
|
||||
|
||||
if adapter_name and not ip_address == current_ip_address:
|
||||
interface_file_name = \
|
||||
'/etc/network/interfaces'
|
||||
# Remove file
|
||||
os.remove(interface_file_name)
|
||||
# Touch file
|
||||
_execute(['touch', interface_file_name])
|
||||
interface_file = open(interface_file_name, 'w')
|
||||
interface_file.write('\nauto %s' % adapter_name)
|
||||
interface_file.write('\niface %s inet static' % adapter_name)
|
||||
interface_file.write('\nbroadcast %s' % broadcast)
|
||||
interface_file.write('\ngateway %s' % gateway)
|
||||
interface_file.write('\nnetmask %s' % subnet_mask)
|
||||
interface_file.write('\naddress %s' % ip_address)
|
||||
interface_file.close()
|
||||
if all_dns_servers:
|
||||
dns_file_name = "/etc/resolv.conf"
|
||||
os.remove(dns_file_name)
|
||||
_execute(['touch', dns_file_name])
|
||||
dns_file = open(dns_file_name, 'w')
|
||||
dns_file.write("; generated by OpenStack guest tools")
|
||||
unique_entries = _filter_duplicates(all_dns_servers)
|
||||
for dns_server in unique_entries:
|
||||
dns_file.write("\nnameserver %s" % dns_server)
|
||||
dns_file.close()
|
||||
print "\nRestarting networking....\n"
|
||||
_execute(['/etc/init.d/networking', 'restart'])
|
||||
|
||||
|
||||
def _linux_set_networking():
|
||||
"""Set IP address for the Linux VM."""
|
||||
vmware_tools_bin = None
|
||||
|
@ -330,8 +374,13 @@ def _linux_set_networking():
|
|||
cmd = [vmware_tools_bin, '--cmd', 'machine.id.get']
|
||||
network_details = _parse_network_details(_execute(cmd,
|
||||
check_exit_code=False))
|
||||
# TODO(sateesh): For other distros like ubuntu, suse, debian, BSD, etc.
|
||||
_set_rhel_networking(network_details)
|
||||
# TODO(sateesh): For other distros like suse, debian, BSD, etc.
|
||||
if(platform.dist()[0] == 'Ubuntu'):
|
||||
_set_ubuntu_networking(network_details)
|
||||
elif (platform.dist()[0] == 'redhat'):
|
||||
_set_rhel_networking(network_details)
|
||||
else:
|
||||
logging.warn(_("Distro '%s' not supported") % platform.dist()[0])
|
||||
else:
|
||||
logging.warn(_("VMware Tools is not installed"))
|
||||
|
||||
|
|
Loading…
Reference in New Issue