Merged trunk
This commit is contained in:
@@ -67,6 +67,7 @@ Lvov Maxim <usrleon@gmail.com>
|
||||
Mark Washenberger <mark.washenberger@rackspace.com>
|
||||
Masanori Itoh <itoumsn@nttdata.co.jp>
|
||||
Matt Dietz <matt.dietz@rackspace.com>
|
||||
Matthew Hooker <matt@cloudscaling.com>
|
||||
Michael Gundlach <michael.gundlach@rackspace.com>
|
||||
Mike Scherbakov <mihgen@gmail.com>
|
||||
Mohammed Naser <mnaser@vexxhost.com>
|
||||
@@ -105,3 +106,4 @@ Yoshiaki Tamura <yoshi@midokura.jp>
|
||||
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
|
||||
Yuriy Taraday <yorik.sar@gmail.com>
|
||||
Zhixue Wu <Zhixue.Wu@citrix.com>
|
||||
Zed Shaw <zedshaw@zedshaw.com>
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Daemon for Nova RRD based instance resource monitoring.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import os
|
||||
import sys
|
||||
from twisted.application import service
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
from nova import twistd
|
||||
from nova.compute import monitor
|
||||
|
||||
LOG = logging.getLogger('nova.instancemonitor')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
twistd.serve(__file__)
|
||||
|
||||
if __name__ == '__builtin__':
|
||||
LOG.warn(_('Starting instance monitor'))
|
||||
# pylint: disable=C0103
|
||||
monitor = monitor.InstanceMonitor()
|
||||
|
||||
# This is the parent service that twistd will be looking for when it
|
||||
# parses this file, return it so that we can get it into globals below
|
||||
application = service.Application('nova-instancemonitor')
|
||||
monitor.setServiceParent(application)
|
||||
@@ -81,7 +81,6 @@ class LogReader(object):
|
||||
if level == 'ERROR':
|
||||
self.handle_logged_error(line)
|
||||
elif level == '[-]' and self.last_error:
|
||||
# twisted stack trace line
|
||||
clean_line = " ".join(line.split(" ")[6:])
|
||||
self.last_error.trace = self.last_error.trace + clean_line
|
||||
else:
|
||||
|
||||
+6
-15
@@ -662,8 +662,9 @@ class NetworkCommands(object):
|
||||
# check for certain required inputs
|
||||
if not label:
|
||||
raise exception.NetworkNotCreated(req='--label')
|
||||
if not fixed_range_v4:
|
||||
raise exception.NetworkNotCreated(req='--fixed_range_v4')
|
||||
if not (fixed_range_v4 or fixed_range_v6):
|
||||
req = '--fixed_range_v4 or --fixed_range_v6'
|
||||
raise exception.NetworkNotCreated(req=req)
|
||||
|
||||
bridge = bridge or FLAGS.flat_network_bridge
|
||||
if not bridge:
|
||||
@@ -689,16 +690,6 @@ class NetworkCommands(object):
|
||||
if FLAGS.network_manager in interface_required:
|
||||
raise exception.NetworkNotCreated(req='--bridge_interface')
|
||||
|
||||
if FLAGS.use_ipv6:
|
||||
fixed_range_v6 = fixed_range_v6 or FLAGS.fixed_range_v6
|
||||
if not fixed_range_v6:
|
||||
raise exception.NetworkNotCreated(req='with use_ipv6, '
|
||||
'--fixed_range_v6')
|
||||
gateway_v6 = gateway_v6 or FLAGS.gateway_v6
|
||||
if not gateway_v6:
|
||||
raise exception.NetworkNotCreated(req='with use_ipv6, '
|
||||
'--gateway_v6')
|
||||
|
||||
# sanitize other input using FLAGS if necessary
|
||||
if not num_networks:
|
||||
num_networks = FLAGS.num_networks
|
||||
@@ -735,8 +726,8 @@ class NetworkCommands(object):
|
||||
def list(self):
|
||||
"""List all created networks"""
|
||||
print "%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (
|
||||
_('network'),
|
||||
_('netmask'),
|
||||
_('IPv4'),
|
||||
_('IPv6'),
|
||||
_('start address'),
|
||||
_('DNS1'),
|
||||
_('DNS2'),
|
||||
@@ -745,7 +736,7 @@ class NetworkCommands(object):
|
||||
for network in db.network_get_all(context.get_admin_context()):
|
||||
print "%-18s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s\t%-15s" % (
|
||||
network.cidr,
|
||||
network.netmask,
|
||||
network.cidr_v6,
|
||||
network.dhcp_start,
|
||||
network.dns1,
|
||||
network.dns2,
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twisted daemon for nova objectstore. Supports S3 API.
|
||||
Daemon for nova objectstore. Supports S3 API.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ if [ "$CMD" == "install" ]; then
|
||||
sudo modprobe kvm
|
||||
sudo /etc/init.d/libvirt-bin restart
|
||||
sudo modprobe nbd
|
||||
sudo apt-get install -y python-twisted python-mox python-ipy python-paste
|
||||
sudo apt-get install -y python-mox python-ipy python-paste
|
||||
sudo apt-get install -y python-migrate python-gflags python-greenlet
|
||||
sudo apt-get install -y python-libvirt python-libxml2 python-routes
|
||||
sudo apt-get install -y python-netaddr python-pastedeploy python-eventlet
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
nova..compute.api.rst
|
||||
nova..compute.instance_types.rst
|
||||
nova..compute.manager.rst
|
||||
nova..compute.monitor.rst
|
||||
nova..compute.power_state.rst
|
||||
nova..console.api.rst
|
||||
nova..console.fake.rst
|
||||
@@ -115,13 +114,11 @@
|
||||
nova..tests.test_scheduler.rst
|
||||
nova..tests.test_service.rst
|
||||
nova..tests.test_test.rst
|
||||
nova..tests.test_twistd.rst
|
||||
nova..tests.test_utils.rst
|
||||
nova..tests.test_virt.rst
|
||||
nova..tests.test_volume.rst
|
||||
nova..tests.test_xenapi.rst
|
||||
nova..tests.xenapi.stubs.rst
|
||||
nova..twistd.rst
|
||||
nova..utils.rst
|
||||
nova..version.rst
|
||||
nova..virt.connection.rst
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
The :mod:`nova..compute.monitor` Module
|
||||
==============================================================================
|
||||
.. automodule:: nova..compute.monitor
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,6 +0,0 @@
|
||||
The :mod:`nova..tests.test_twistd` Module
|
||||
==============================================================================
|
||||
.. automodule:: nova..tests.test_twistd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -1,6 +0,0 @@
|
||||
The :mod:`nova..twistd` Module
|
||||
==============================================================================
|
||||
.. automodule:: nova..twistd
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -21,7 +21,6 @@ Generating source/api/nova..cloudpipe.pipelib.rst
|
||||
Generating source/api/nova..compute.disk.rst
|
||||
Generating source/api/nova..compute.instance_types.rst
|
||||
Generating source/api/nova..compute.manager.rst
|
||||
Generating source/api/nova..compute.monitor.rst
|
||||
Generating source/api/nova..compute.power_state.rst
|
||||
Generating source/api/nova..context.rst
|
||||
Generating source/api/nova..crypto.rst
|
||||
@@ -79,11 +78,9 @@ Generating source/api/nova..tests.rpc_unittest.rst
|
||||
Generating source/api/nova..tests.runtime_flags.rst
|
||||
Generating source/api/nova..tests.scheduler_unittest.rst
|
||||
Generating source/api/nova..tests.service_unittest.rst
|
||||
Generating source/api/nova..tests.twistd_unittest.rst
|
||||
Generating source/api/nova..tests.validator_unittest.rst
|
||||
Generating source/api/nova..tests.virt_unittest.rst
|
||||
Generating source/api/nova..tests.volume_unittest.rst
|
||||
Generating source/api/nova..twistd.rst
|
||||
Generating source/api/nova..utils.rst
|
||||
Generating source/api/nova..validate.rst
|
||||
Generating source/api/nova..virt.connection.rst
|
||||
|
||||
@@ -45,7 +45,7 @@ Below you will find a helpful explanation of the different components.
|
||||
* Web Dashboard: potential external component that talks to the api
|
||||
* api: component that receives http requests, converts commands and communicates with other components via the queue or http (in the case of objectstore)
|
||||
* Auth Manager: component responsible for users/projects/and roles. Can backend to DB or LDAP. This is not a separate binary, but rather a python class that is used by most components in the system.
|
||||
* objectstore: twisted http server that replicates s3 api and allows storage and retrieval of images
|
||||
* objectstore: http server that replicates s3 api and allows storage and retrieval of images
|
||||
* scheduler: decides which host gets each vm and volume
|
||||
* volume: manages dynamically attachable block devices.
|
||||
* network: manages ip forwarding, bridges, and vlans
|
||||
|
||||
@@ -118,19 +118,6 @@ The :mod:`nova.virt.fake` Driver
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Monitoring
|
||||
----------
|
||||
|
||||
The :mod:`nova.compute.monitor` Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automodule:: nova.compute.monitor
|
||||
:noindex:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ To activate the Nova virtualenv for the extent of your current shell session
|
||||
|
||||
Also, make test will automatically use the virtualenv.
|
||||
|
||||
If you don't want to create a virtualenv every time you branch (which takes a while as long as we have the large Twisted project as a dependency) you can reuse a single virtualenv for all branches.
|
||||
If you don't want to create a virtualenv every time you branch you can reuse a single virtualenv for all branches.
|
||||
|
||||
#. If you don't have a nova/ directory containing trunk/ and other branches, do so now.
|
||||
#. Go into nova/trunk and install a virtualenv.
|
||||
|
||||
@@ -102,16 +102,6 @@ The :mod:`nova.test` Module
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
The :mod:`nova.twistd` Module
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: nova.twistd
|
||||
:noindex:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
The :mod:`nova.utils` Module
|
||||
----------------------------
|
||||
|
||||
@@ -215,16 +205,6 @@ The :mod:`runtime_flags` Module
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
The :mod:`twistd_unittest` Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. automodule:: nova.tests.twistd_unittest
|
||||
:noindex:
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
The :mod:`validator_unittest` Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
+2
-1
@@ -107,7 +107,8 @@ class DelegatedAuthMiddleware(wsgi.Middleware):
|
||||
def process_request(self, request):
|
||||
os_user = request.headers['X-OpenStack-User']
|
||||
os_project = request.headers['X-OpenStack-Project']
|
||||
context_ref = context.RequestContext(user=os_user, project=os_project)
|
||||
context_ref = context.RequestContext(user_id=os_user,
|
||||
project_id=os_project)
|
||||
request.environ['openstack.context'] = context_ref
|
||||
|
||||
|
||||
|
||||
+17
-15
@@ -66,7 +66,7 @@ class RequestLogging(wsgi.Middleware):
|
||||
else:
|
||||
controller = None
|
||||
action = None
|
||||
ctxt = request.environ.get('ec2.context', None)
|
||||
ctxt = request.environ.get('nova.context', None)
|
||||
delta = utils.utcnow() - start
|
||||
seconds = delta.seconds
|
||||
microseconds = delta.microseconds
|
||||
@@ -139,8 +139,7 @@ class Lockout(wsgi.Middleware):
|
||||
|
||||
|
||||
class Authenticate(wsgi.Middleware):
|
||||
|
||||
"""Authenticate an EC2 request and add 'ec2.context' to WSGI environ."""
|
||||
"""Authenticate an EC2 request and add 'nova.context' to WSGI environ."""
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
@@ -157,8 +156,9 @@ class Authenticate(wsgi.Middleware):
|
||||
auth_params.pop('Signature')
|
||||
|
||||
# Authenticate the request.
|
||||
authman = manager.AuthManager()
|
||||
try:
|
||||
(user, project) = manager.AuthManager().authenticate(
|
||||
(user, project) = authman.authenticate(
|
||||
access,
|
||||
signature,
|
||||
auth_params,
|
||||
@@ -174,14 +174,17 @@ class Authenticate(wsgi.Middleware):
|
||||
remote_address = req.remote_addr
|
||||
if FLAGS.use_forwarded_for:
|
||||
remote_address = req.headers.get('X-Forwarded-For', remote_address)
|
||||
ctxt = context.RequestContext(user=user,
|
||||
project=project,
|
||||
roles = authman.get_active_roles(user, project)
|
||||
ctxt = context.RequestContext(user_id=user.id,
|
||||
project_id=project.id,
|
||||
is_admin=user.is_admin(),
|
||||
roles=roles,
|
||||
remote_address=remote_address)
|
||||
req.environ['ec2.context'] = ctxt
|
||||
req.environ['nova.context'] = ctxt
|
||||
uname = user.name
|
||||
pname = project.name
|
||||
msg = _('Authenticated Request For %(uname)s:%(pname)s)') % locals()
|
||||
LOG.audit(msg, context=req.environ['ec2.context'])
|
||||
LOG.audit(msg, context=req.environ['nova.context'])
|
||||
return self.application
|
||||
|
||||
|
||||
@@ -228,7 +231,7 @@ class Authorizer(wsgi.Middleware):
|
||||
"""Authorize an EC2 API request.
|
||||
|
||||
Return a 401 if ec2.controller and ec2.action in WSGI environ may not be
|
||||
executed in ec2.context.
|
||||
executed in nova.context.
|
||||
"""
|
||||
|
||||
def __init__(self, application):
|
||||
@@ -282,7 +285,7 @@ class Authorizer(wsgi.Middleware):
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
context = req.environ['ec2.context']
|
||||
context = req.environ['nova.context']
|
||||
controller = req.environ['ec2.request'].controller.__class__.__name__
|
||||
action = req.environ['ec2.request'].action
|
||||
allowed_roles = self.action_roles[controller].get(action, ['none'])
|
||||
@@ -295,28 +298,27 @@ class Authorizer(wsgi.Middleware):
|
||||
|
||||
def _matches_any_role(self, context, roles):
|
||||
"""Return True if any role in roles is allowed in context."""
|
||||
if context.user.is_superuser():
|
||||
if context.is_admin:
|
||||
return True
|
||||
if 'all' in roles:
|
||||
return True
|
||||
if 'none' in roles:
|
||||
return False
|
||||
return any(context.project.has_role(context.user_id, role)
|
||||
for role in roles)
|
||||
return any(role in context.roles for role in roles)
|
||||
|
||||
|
||||
class Executor(wsgi.Application):
|
||||
|
||||
"""Execute an EC2 API request.
|
||||
|
||||
Executes 'ec2.action' upon 'ec2.controller', passing 'ec2.context' and
|
||||
Executes 'ec2.action' upon 'ec2.controller', passing 'nova.context' and
|
||||
'ec2.action_args' (all variables in WSGI environ.) Returns an XML
|
||||
response, or a 400 upon failure.
|
||||
"""
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
context = req.environ['ec2.context']
|
||||
context = req.environ['nova.context']
|
||||
api_request = req.environ['ec2.request']
|
||||
result = None
|
||||
try:
|
||||
|
||||
@@ -40,6 +40,7 @@ from nova.api.openstack import servers
|
||||
from nova.api.openstack import server_metadata
|
||||
from nova.api.openstack import shared_ip_groups
|
||||
from nova.api.openstack import users
|
||||
from nova.api.openstack import versions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import zones
|
||||
|
||||
@@ -96,6 +97,7 @@ class APIRouter(base_wsgi.Router):
|
||||
server_members['suspend'] = 'POST'
|
||||
server_members['resume'] = 'POST'
|
||||
server_members['rescue'] = 'POST'
|
||||
server_members['migrate'] = 'POST'
|
||||
server_members['unrescue'] = 'POST'
|
||||
server_members['reset_network'] = 'POST'
|
||||
server_members['inject_network_info'] = 'POST'
|
||||
@@ -115,6 +117,10 @@ class APIRouter(base_wsgi.Router):
|
||||
'select': 'POST',
|
||||
'boot': 'POST'})
|
||||
|
||||
mapper.connect("versions", "/",
|
||||
controller=versions.create_resource(version),
|
||||
action='show')
|
||||
|
||||
mapper.resource("console", "consoles",
|
||||
controller=consoles.create_resource(),
|
||||
parent_resource=dict(member_name='server',
|
||||
|
||||
+16
-12
@@ -48,31 +48,35 @@ class AuthMiddleware(wsgi.Middleware):
|
||||
def __call__(self, req):
|
||||
if not self.has_authentication(req):
|
||||
return self.authenticate(req)
|
||||
user = self.get_user_by_authentication(req)
|
||||
if not user:
|
||||
user_id = self.get_user_by_authentication(req)
|
||||
if not user_id:
|
||||
token = req.headers["X-Auth-Token"]
|
||||
msg = _("%(user)s could not be found with token '%(token)s'")
|
||||
msg = _("%(user_id)s could not be found with token '%(token)s'")
|
||||
LOG.warn(msg % locals())
|
||||
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||
|
||||
try:
|
||||
account = req.headers["X-Auth-Project-Id"]
|
||||
project_id = req.headers["X-Auth-Project-Id"]
|
||||
except KeyError:
|
||||
# FIXME(usrleon): It needed only for compatibility
|
||||
# while osapi clients don't use this header
|
||||
accounts = self.auth.get_projects(user=user)
|
||||
if accounts:
|
||||
account = accounts[0]
|
||||
projects = self.auth.get_projects(user_id)
|
||||
if projects:
|
||||
project_id = projects[0].id
|
||||
else:
|
||||
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||
|
||||
if not self.auth.is_admin(user) and \
|
||||
not self.auth.is_project_member(user, account):
|
||||
msg = _("%(user)s must be an admin or a member of %(account)s")
|
||||
is_admin = self.auth.is_admin(user_id)
|
||||
req.environ['nova.context'] = context.RequestContext(user_id,
|
||||
project_id,
|
||||
is_admin)
|
||||
if not is_admin and not self.auth.is_project_member(user_id,
|
||||
project_id):
|
||||
msg = _("%(user_id)s must be an admin or a "
|
||||
"member of %(project_id)s")
|
||||
LOG.warn(msg % locals())
|
||||
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||
|
||||
req.environ['nova.context'] = context.RequestContext(user, account)
|
||||
return self.application
|
||||
|
||||
def has_authentication(self, req):
|
||||
@@ -133,7 +137,7 @@ class AuthMiddleware(wsgi.Middleware):
|
||||
if delta.days >= 2:
|
||||
self.db.auth_token_destroy(ctxt, token['token_hash'])
|
||||
else:
|
||||
return self.auth.get_user(token['user_id'])
|
||||
return token['user_id']
|
||||
return None
|
||||
|
||||
def _authorize_user(self, username, key, req):
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
|
||||
import re
|
||||
from urlparse import urlparse
|
||||
from xml.dom import minidom
|
||||
|
||||
import webob
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.api.openstack.common')
|
||||
@@ -192,3 +194,83 @@ def get_version_from_href(href):
|
||||
except IndexError:
|
||||
version = '1.0'
|
||||
return version
|
||||
|
||||
|
||||
class MetadataXMLDeserializer(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 MetadataHeadersSerializer(wsgi.ResponseHeadersSerializer):
|
||||
|
||||
def delete(self, response, data):
|
||||
response.status_int = 204
|
||||
|
||||
|
||||
class MetadataXMLSerializer(wsgi.XMLDictSerializer):
|
||||
def __init__(self, xmlns=wsgi.XMLNS_V11):
|
||||
super(MetadataXMLSerializer, self).__init__(xmlns=xmlns)
|
||||
|
||||
def _meta_item_to_xml(self, doc, key, value):
|
||||
node = doc.createElement('meta')
|
||||
doc.appendChild(node)
|
||||
node.setAttribute('key', '%s' % key)
|
||||
text = doc.createTextNode('%s' % value)
|
||||
node.appendChild(text)
|
||||
return node
|
||||
|
||||
def meta_list_to_xml(self, xml_doc, meta_items):
|
||||
container_node = xml_doc.createElement('metadata')
|
||||
for (key, value) in meta_items:
|
||||
item_node = self._meta_item_to_xml(xml_doc, key, value)
|
||||
container_node.appendChild(item_node)
|
||||
return container_node
|
||||
|
||||
def _meta_list_to_xml_string(self, metadata_dict):
|
||||
xml_doc = minidom.Document()
|
||||
items = metadata_dict['metadata'].items()
|
||||
container_node = self.meta_list_to_xml(xml_doc, items)
|
||||
xml_doc.appendChild(container_node)
|
||||
self._add_xmlns(container_node)
|
||||
return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
|
||||
|
||||
def index(self, metadata_dict):
|
||||
return self._meta_list_to_xml_string(metadata_dict)
|
||||
|
||||
def create(self, metadata_dict):
|
||||
return self._meta_list_to_xml_string(metadata_dict)
|
||||
|
||||
def 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]
|
||||
item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
|
||||
xml_doc.appendChild(item_node)
|
||||
self._add_xmlns(item_node)
|
||||
return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
|
||||
|
||||
def show(self, meta_item_dict):
|
||||
return self._meta_item_to_xml_string(meta_item_dict['meta'])
|
||||
|
||||
def update(self, meta_item_dict):
|
||||
return self._meta_item_to_xml_string(meta_item_dict['meta'])
|
||||
|
||||
def default(self, *args, **kwargs):
|
||||
return ''
|
||||
|
||||
@@ -27,9 +27,9 @@ from nova.api.openstack import extensions
|
||||
def _translate_floating_ip_view(floating_ip):
|
||||
result = {'id': floating_ip['id'],
|
||||
'ip': floating_ip['address']}
|
||||
if 'fixed_ip' in floating_ip:
|
||||
try:
|
||||
result['fixed_ip'] = floating_ip['fixed_ip']['address']
|
||||
else:
|
||||
except (TypeError, KeyError):
|
||||
result['fixed_ip'] = None
|
||||
if 'instance' in floating_ip:
|
||||
result['instance_id'] = floating_ip['instance']['id']
|
||||
|
||||
@@ -20,6 +20,7 @@ import webob
|
||||
from webob import exc
|
||||
from xml.dom import minidom
|
||||
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
@@ -29,7 +30,6 @@ from nova import utils
|
||||
|
||||
from nova.compute import instance_types
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.auth import manager as auth_manager
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.api.openstack.create_instance_helper')
|
||||
@@ -80,13 +80,21 @@ class CreateInstanceHelper(object):
|
||||
|
||||
key_name = None
|
||||
key_data = None
|
||||
key_pairs = auth_manager.AuthManager.get_key_pairs(context)
|
||||
# TODO(vish): Key pair access should move into a common library
|
||||
# instead of being accessed directly from the db.
|
||||
key_pairs = db.key_pair_get_all_by_user(context.elevated(),
|
||||
context.user_id)
|
||||
if key_pairs:
|
||||
key_pair = key_pairs[0]
|
||||
key_name = key_pair['name']
|
||||
key_data = key_pair['public_key']
|
||||
|
||||
image_href = self.controller._image_ref_from_req_data(body)
|
||||
# If the image href was generated by nova api, strip image_href
|
||||
# down to an id and use the default glance connection params
|
||||
|
||||
if str(image_href).startswith(req.application_url):
|
||||
image_href = image_href.split('/').pop()
|
||||
try:
|
||||
image_service, image_id = nova.image.get_image_service(image_href)
|
||||
kernel_id, ramdisk_id = self._get_kernel_ramdisk_from_image(
|
||||
@@ -188,7 +196,7 @@ class CreateInstanceHelper(object):
|
||||
Overrides normal behavior in the case of xml content
|
||||
"""
|
||||
if request.content_type == "application/xml":
|
||||
deserializer = ServerCreateRequestXMLDeserializer()
|
||||
deserializer = ServerXMLDeserializer()
|
||||
return deserializer.deserialize(request.body)
|
||||
else:
|
||||
return self._deserialize(request.body, request.get_content_type())
|
||||
@@ -293,6 +301,37 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
and personality attributes
|
||||
"""
|
||||
|
||||
def action(self, string):
|
||||
dom = minidom.parseString(string)
|
||||
action_node = dom.childNodes[0]
|
||||
action_name = action_node.tagName
|
||||
|
||||
action_deserializer = {
|
||||
'createImage': self._action_create_image,
|
||||
'createBackup': self._action_create_backup,
|
||||
}.get(action_name, self.default)
|
||||
|
||||
action_data = action_deserializer(action_node)
|
||||
|
||||
return {'body': {action_name: action_data}}
|
||||
|
||||
def _action_create_image(self, node):
|
||||
return self._deserialize_image_action(node, ('name',))
|
||||
|
||||
def _action_create_backup(self, node):
|
||||
attributes = ('name', 'backup_type', 'rotation')
|
||||
return self._deserialize_image_action(node, attributes)
|
||||
|
||||
def _deserialize_image_action(self, node, allowed_attributes):
|
||||
data = {}
|
||||
for attribute in allowed_attributes:
|
||||
value = node.getAttribute(attribute)
|
||||
if value:
|
||||
data[attribute] = value
|
||||
metadata_node = self.find_first_child_named(node, 'metadata')
|
||||
data['metadata'] = self.extract_metadata(metadata_node)
|
||||
return data
|
||||
|
||||
def create(self, string):
|
||||
"""Deserialize an xml-formatted server create request"""
|
||||
dom = minidom.parseString(string)
|
||||
@@ -303,29 +342,29 @@ class ServerXMLDeserializer(wsgi.MetadataXMLDeserializer):
|
||||
"""Marshal the server attribute of a parsed request"""
|
||||
server = {}
|
||||
server_node = self.find_first_child_named(node, 'server')
|
||||
for attr in ["name", "imageId", "flavorId", "imageRef", "flavorRef"]:
|
||||
|
||||
attributes = ["name", "imageId", "flavorId", "imageRef",
|
||||
"flavorRef", "adminPass"]
|
||||
for attr in attributes:
|
||||
if server_node.getAttribute(attr):
|
||||
server[attr] = server_node.getAttribute(attr)
|
||||
|
||||
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)
|
||||
if personality is not None:
|
||||
server["personality"] = personality
|
||||
server["metadata"] = self.extract_metadata(metadata_node)
|
||||
|
||||
server["personality"] = self._extract_personality(server_node)
|
||||
|
||||
return server
|
||||
|
||||
def _extract_personality(self, server_node):
|
||||
"""Marshal the personality attribute of a parsed request"""
|
||||
personality_node = \
|
||||
self.find_first_child_named(server_node, "personality")
|
||||
if personality_node is None:
|
||||
return None
|
||||
node = self.find_first_child_named(server_node, "personality")
|
||||
personality = []
|
||||
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)
|
||||
personality.append(item)
|
||||
if node is not None:
|
||||
for file_node in self.find_children_named(node, "file"):
|
||||
item = {}
|
||||
if file_node.hasAttribute("path"):
|
||||
item["path"] = file_node.getAttribute("path")
|
||||
item["contents"] = self.extract_text(file_node)
|
||||
personality.append(item)
|
||||
return personality
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
# under the License.
|
||||
|
||||
from webob import exc
|
||||
from xml.dom import minidom
|
||||
|
||||
from nova import flags
|
||||
from nova import image
|
||||
from nova import quota
|
||||
from nova import utils
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
@@ -118,95 +118,15 @@ 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)
|
||||
|
||||
def _meta_item_to_xml(self, doc, key, value):
|
||||
node = doc.createElement('meta')
|
||||
doc.appendChild(node)
|
||||
node.setAttribute('key', '%s' % key)
|
||||
text = doc.createTextNode('%s' % value)
|
||||
node.appendChild(text)
|
||||
return node
|
||||
|
||||
def meta_list_to_xml(self, xml_doc, meta_items):
|
||||
container_node = xml_doc.createElement('metadata')
|
||||
for (key, value) in meta_items:
|
||||
item_node = self._meta_item_to_xml(xml_doc, key, value)
|
||||
container_node.appendChild(item_node)
|
||||
return container_node
|
||||
|
||||
def _meta_list_to_xml_string(self, metadata_dict):
|
||||
xml_doc = minidom.Document()
|
||||
items = metadata_dict['metadata'].items()
|
||||
container_node = self.meta_list_to_xml(xml_doc, items)
|
||||
xml_doc.appendChild(container_node)
|
||||
self._add_xmlns(container_node)
|
||||
return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
|
||||
|
||||
def index(self, metadata_dict):
|
||||
return self._meta_list_to_xml_string(metadata_dict)
|
||||
|
||||
def create(self, metadata_dict):
|
||||
return self._meta_list_to_xml_string(metadata_dict)
|
||||
|
||||
def 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]
|
||||
item_node = self._meta_item_to_xml(xml_doc, item_key, item_value)
|
||||
xml_doc.appendChild(item_node)
|
||||
self._add_xmlns(item_node)
|
||||
return xml_doc.toprettyxml(indent=' ', encoding='UTF-8')
|
||||
|
||||
def show(self, meta_item_dict):
|
||||
return self._meta_item_to_xml_string(meta_item_dict['meta'])
|
||||
|
||||
def update(self, meta_item_dict):
|
||||
return self._meta_item_to_xml_string(meta_item_dict['meta'])
|
||||
|
||||
def default(self, *args, **kwargs):
|
||||
return ''
|
||||
|
||||
|
||||
def create_resource():
|
||||
headers_serializer = HeadersSerializer()
|
||||
headers_serializer = common.MetadataHeadersSerializer()
|
||||
|
||||
body_deserializers = {
|
||||
'application/xml': ImageMetadataXMLDeserializer(),
|
||||
'application/xml': common.MetadataXMLDeserializer(),
|
||||
}
|
||||
|
||||
body_serializers = {
|
||||
'application/xml': ImageMetadataXMLSerializer(),
|
||||
'application/xml': common.MetadataXMLSerializer(),
|
||||
}
|
||||
serializer = wsgi.ResponseSerializer(body_serializers, headers_serializer)
|
||||
deserializer = wsgi.RequestDeserializer(body_deserializers)
|
||||
|
||||
+23
-110
@@ -98,79 +98,34 @@ class Controller(object):
|
||||
self._image_service.delete(context, id)
|
||||
return webob.exc.HTTPNoContent()
|
||||
|
||||
def create(self, req, body):
|
||||
"""Snapshot or backup a server instance and save the image.
|
||||
|
||||
Images now have an `image_type` associated with them, which can be
|
||||
'snapshot' or the backup type, like 'daily' or 'weekly'.
|
||||
|
||||
If the image_type is backup-like, then the rotation factor can be
|
||||
included and that will cause the oldest backups that exceed the
|
||||
rotation factor to be deleted.
|
||||
|
||||
:param req: `wsgi.Request` object
|
||||
"""
|
||||
def get_param(param):
|
||||
try:
|
||||
return body["image"][param]
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest(explanation="Missing required "
|
||||
"param: %s" % param)
|
||||
|
||||
context = req.environ['nova.context']
|
||||
content_type = req.get_content_type()
|
||||
|
||||
if not body:
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
image_type = body["image"].get("image_type", "snapshot")
|
||||
|
||||
try:
|
||||
server_id = self._server_id_from_req(req, body)
|
||||
except KeyError:
|
||||
raise webob.exc.HTTPBadRequest()
|
||||
|
||||
image_name = get_param("name")
|
||||
props = self._get_extra_properties(req, body)
|
||||
|
||||
if image_type == "snapshot":
|
||||
image = self._compute_service.snapshot(
|
||||
context, server_id, image_name,
|
||||
extra_properties=props)
|
||||
elif image_type == "backup":
|
||||
# NOTE(sirp): Unlike snapshot, backup is not a customer facing
|
||||
# API call; rather, it's used by the internal backup scheduler
|
||||
if not FLAGS.allow_admin_api:
|
||||
raise webob.exc.HTTPBadRequest(
|
||||
explanation="Admin API Required")
|
||||
|
||||
backup_type = get_param("backup_type")
|
||||
rotation = int(get_param("rotation"))
|
||||
|
||||
image = self._compute_service.backup(
|
||||
context, server_id, image_name,
|
||||
backup_type, rotation, extra_properties=props)
|
||||
else:
|
||||
LOG.error(_("Invalid image_type '%s' passed") % image_type)
|
||||
raise webob.exc.HTTPBadRequest(explanation="Invalue image_type: "
|
||||
"%s" % image_type)
|
||||
|
||||
return dict(image=self.get_builder(req).build(image, detail=True))
|
||||
|
||||
def get_builder(self, request):
|
||||
"""Indicates that you must use a Controller subclass."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _server_id_from_req(self, req, data):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_extra_properties(self, req, data):
|
||||
return {}
|
||||
|
||||
|
||||
class ControllerV10(Controller):
|
||||
"""Version 1.0 specific controller logic."""
|
||||
|
||||
def create(self, req, body):
|
||||
"""Snapshot a server instance and save the image."""
|
||||
try:
|
||||
image = body["image"]
|
||||
except (KeyError, TypeError):
|
||||
msg = _("Invalid image entity")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
image_name = image["name"]
|
||||
server_id = image["serverId"]
|
||||
except KeyError as missing_key:
|
||||
msg = _("Image entity requires %s") % missing_key
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
context = req.environ["nova.context"]
|
||||
image = self._compute_service.snapshot(context, server_id, image_name)
|
||||
|
||||
return dict(image=self.get_builder(req).build(image, detail=True))
|
||||
|
||||
def get_builder(self, request):
|
||||
"""Property to get the ViewBuilder class we need to use."""
|
||||
base_url = request.application_url
|
||||
@@ -202,13 +157,6 @@ class ControllerV10(Controller):
|
||||
builder = self.get_builder(req).build
|
||||
return dict(images=[builder(image, detail=True) for image in images])
|
||||
|
||||
def _server_id_from_req(self, req, data):
|
||||
try:
|
||||
return data['image']['serverId']
|
||||
except KeyError:
|
||||
msg = _("Expected serverId attribute on server entity.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
|
||||
class ControllerV11(Controller):
|
||||
"""Version 1.1 specific controller logic."""
|
||||
@@ -246,37 +194,8 @@ class ControllerV11(Controller):
|
||||
builder = self.get_builder(req).build
|
||||
return dict(images=[builder(image, detail=True) for image in images])
|
||||
|
||||
def _server_id_from_req(self, req, data):
|
||||
try:
|
||||
server_ref = data['image']['serverRef']
|
||||
except KeyError:
|
||||
msg = _("Expected serverRef attribute on server entity.")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
if not server_ref.startswith('http'):
|
||||
return server_ref
|
||||
|
||||
passed = urlparse.urlparse(server_ref)
|
||||
expected = urlparse.urlparse(req.application_url)
|
||||
version = expected.path.split('/')[1]
|
||||
expected_prefix = "/%s/servers/" % version
|
||||
_empty, _sep, server_id = passed.path.partition(expected_prefix)
|
||||
scheme_ok = passed.scheme == expected.scheme
|
||||
host_ok = passed.hostname == expected.hostname
|
||||
port_ok = (passed.port == expected.port or
|
||||
passed.port == FLAGS.osapi_port)
|
||||
if not (scheme_ok and port_ok and host_ok and server_id):
|
||||
msg = _("serverRef must match request url")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return server_id
|
||||
|
||||
def _get_extra_properties(self, req, data):
|
||||
server_ref = data['image']['serverRef']
|
||||
if not server_ref.startswith('http'):
|
||||
server_ref = os.path.join(req.application_url, 'servers',
|
||||
server_ref)
|
||||
return {'instance_ref': server_ref}
|
||||
def create(self, *args, **kwargs):
|
||||
raise webob.exc.HTTPMethodNotAllowed()
|
||||
|
||||
|
||||
class ImageXMLSerializer(wsgi.XMLDictSerializer):
|
||||
@@ -284,7 +203,7 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer):
|
||||
xmlns = wsgi.XMLNS_V11
|
||||
|
||||
def __init__(self):
|
||||
self.metadata_serializer = image_metadata.ImageMetadataXMLSerializer()
|
||||
self.metadata_serializer = common.MetadataXMLSerializer()
|
||||
|
||||
def _image_to_xml(self, xml_doc, image):
|
||||
image_node = xml_doc.createElement('image')
|
||||
@@ -369,12 +288,6 @@ class ImageXMLSerializer(wsgi.XMLDictSerializer):
|
||||
image_dict['image'])
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
def create(self, image_dict):
|
||||
xml_doc = minidom.Document()
|
||||
node = self._image_to_xml_detailed(xml_doc,
|
||||
image_dict['image'])
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
|
||||
def create_resource(version='1.0'):
|
||||
controller = {
|
||||
|
||||
+301
-43
@@ -14,26 +14,29 @@
|
||||
# under the License.
|
||||
|
||||
import base64
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from webob import exc
|
||||
from xml.dom import minidom
|
||||
import webob
|
||||
|
||||
from nova import compute
|
||||
from nova import db
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
from nova.api.openstack import common
|
||||
from nova.api.openstack import create_instance_helper as helper
|
||||
from nova.api.openstack import ips
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.compute import instance_types
|
||||
from nova.scheduler import api as scheduler_api
|
||||
import nova.api.openstack
|
||||
import nova.api.openstack.views.addresses
|
||||
import nova.api.openstack.views.flavors
|
||||
import nova.api.openstack.views.images
|
||||
import nova.api.openstack.views.servers
|
||||
from nova.api.openstack import wsgi
|
||||
import nova.api.openstack
|
||||
from nova.scheduler import api as scheduler_api
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.api.openstack.servers')
|
||||
@@ -152,23 +155,94 @@ class Controller(object):
|
||||
|
||||
@scheduler_api.redirect_handler
|
||||
def action(self, req, id, body):
|
||||
"""Multi-purpose method used to reboot, rebuild, or
|
||||
resize a server"""
|
||||
"""Multi-purpose method used to take actions on a server"""
|
||||
|
||||
actions = {
|
||||
self.actions = {
|
||||
'changePassword': self._action_change_password,
|
||||
'reboot': self._action_reboot,
|
||||
'resize': self._action_resize,
|
||||
'confirmResize': self._action_confirm_resize,
|
||||
'revertResize': self._action_revert_resize,
|
||||
'rebuild': self._action_rebuild,
|
||||
'migrate': self._action_migrate}
|
||||
'createImage': self._action_create_image,
|
||||
}
|
||||
|
||||
for key in actions.keys():
|
||||
if FLAGS.allow_admin_api:
|
||||
admin_actions = {
|
||||
'createBackup': self._action_create_backup,
|
||||
}
|
||||
self.actions.update(admin_actions)
|
||||
|
||||
for key in self.actions.keys():
|
||||
if key in body:
|
||||
return actions[key](body, req, id)
|
||||
return self.actions[key](body, req, id)
|
||||
|
||||
raise exc.HTTPNotImplemented()
|
||||
|
||||
def _action_create_backup(self, input_dict, req, instance_id):
|
||||
"""Backup a server instance.
|
||||
|
||||
Images now have an `image_type` associated with them, which can be
|
||||
'snapshot' or the backup type, like 'daily' or 'weekly'.
|
||||
|
||||
If the image_type is backup-like, then the rotation factor can be
|
||||
included and that will cause the oldest backups that exceed the
|
||||
rotation factor to be deleted.
|
||||
|
||||
"""
|
||||
entity = input_dict["createBackup"]
|
||||
|
||||
try:
|
||||
image_name = entity["name"]
|
||||
backup_type = entity["backup_type"]
|
||||
rotation = entity["rotation"]
|
||||
|
||||
except KeyError as missing_key:
|
||||
msg = _("createBackup entity requires %s attribute") % missing_key
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
except TypeError:
|
||||
msg = _("Malformed createBackup entity")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
rotation = int(rotation)
|
||||
except ValueError:
|
||||
msg = _("createBackup attribute 'rotation' must be an integer")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
# preserve link to server in image properties
|
||||
server_ref = os.path.join(req.application_url,
|
||||
'servers',
|
||||
str(instance_id))
|
||||
props = {'instance_ref': server_ref}
|
||||
|
||||
metadata = entity.get('metadata', {})
|
||||
try:
|
||||
props.update(metadata)
|
||||
except ValueError:
|
||||
msg = _("Invalid metadata")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
context = req.environ["nova.context"]
|
||||
image = self.compute_api.backup(context,
|
||||
instance_id,
|
||||
image_name,
|
||||
backup_type,
|
||||
rotation,
|
||||
extra_properties=props)
|
||||
|
||||
# build location of newly-created image entity
|
||||
image_id = str(image['id'])
|
||||
image_ref = os.path.join(req.application_url, 'images', image_id)
|
||||
|
||||
resp = webob.Response(status_int=202)
|
||||
resp.headers['Location'] = image_ref
|
||||
return resp
|
||||
|
||||
def _action_create_image(self, input_dict, req, id):
|
||||
return exc.HTTPNotImplemented()
|
||||
|
||||
def _action_change_password(self, input_dict, req, id):
|
||||
return exc.HTTPNotImplemented()
|
||||
|
||||
@@ -206,14 +280,6 @@ class Controller(object):
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
def _action_migrate(self, input_dict, req, id):
|
||||
try:
|
||||
self.compute_api.resize(req.environ['nova.context'], id)
|
||||
except Exception, e:
|
||||
LOG.exception(_("Error in migrate %s"), e)
|
||||
raise exc.HTTPBadRequest()
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@scheduler_api.redirect_handler
|
||||
def lock(self, req, id):
|
||||
"""
|
||||
@@ -339,6 +405,15 @@ class Controller(object):
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@scheduler_api.redirect_handler
|
||||
def migrate(self, req, id):
|
||||
try:
|
||||
self.compute_api.resize(req.environ['nova.context'], id)
|
||||
except Exception, e:
|
||||
LOG.exception(_("Error in migrate %s"), e)
|
||||
raise exc.HTTPBadRequest()
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
@scheduler_api.redirect_handler
|
||||
def rescue(self, req, id):
|
||||
"""Permit users to rescue the server."""
|
||||
@@ -403,6 +478,24 @@ class Controller(object):
|
||||
error=item.error))
|
||||
return dict(actions=actions)
|
||||
|
||||
def resize(self, req, instance_id, flavor_id):
|
||||
"""Begin the resize process with given instance/flavor."""
|
||||
context = req.environ["nova.context"]
|
||||
|
||||
try:
|
||||
self.compute_api.resize(context, instance_id, flavor_id)
|
||||
except exception.FlavorNotFound:
|
||||
msg = _("Unable to locate requested flavor.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
except exception.CannotResizeToSameSize:
|
||||
msg = _("Resize requires a change in size.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
except exception.CannotResizeToSmallerSize:
|
||||
msg = _("Resizing to a smaller size is not supported.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
|
||||
class ControllerV10(Controller):
|
||||
|
||||
@@ -436,14 +529,13 @@ class ControllerV10(Controller):
|
||||
|
||||
def _action_resize(self, input_dict, req, id):
|
||||
""" Resizes a given instance to the flavor size requested """
|
||||
if 'resize' in input_dict and 'flavorId' in input_dict['resize']:
|
||||
flavor_id = input_dict['resize']['flavorId']
|
||||
self.compute_api.resize(req.environ['nova.context'], id,
|
||||
flavor_id)
|
||||
else:
|
||||
LOG.exception(_("Missing 'flavorId' argument for resize"))
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
return webob.Response(status_int=202)
|
||||
try:
|
||||
flavor_id = input_dict["resize"]["flavorId"]
|
||||
except (KeyError, TypeError):
|
||||
msg = _("Resize requests require 'flavorId' attribute.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return self.resize(req, id, flavor_id)
|
||||
|
||||
def _action_rebuild(self, info, request, instance_id):
|
||||
context = request.environ['nova.context']
|
||||
@@ -480,11 +572,20 @@ class ControllerV11(Controller):
|
||||
raise exc.HTTPNotFound()
|
||||
|
||||
def _image_ref_from_req_data(self, data):
|
||||
return data['server']['imageRef']
|
||||
try:
|
||||
return data['server']['imageRef']
|
||||
except (TypeError, KeyError):
|
||||
msg = _("Missing imageRef attribute")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
def _flavor_id_from_req_data(self, data):
|
||||
href = data['server']['flavorRef']
|
||||
return common.get_id_from_href(href)
|
||||
try:
|
||||
flavor_ref = data['server']['flavorRef']
|
||||
except (TypeError, KeyError):
|
||||
msg = _("Missing flavorRef attribute")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return common.get_id_from_href(flavor_ref)
|
||||
|
||||
def _build_view(self, req, instance, is_detail=False):
|
||||
base_url = req.application_url
|
||||
@@ -544,18 +645,12 @@ class ControllerV11(Controller):
|
||||
def _action_resize(self, input_dict, req, id):
|
||||
""" Resizes a given instance to the flavor size requested """
|
||||
try:
|
||||
if 'resize' in input_dict and 'flavorRef' in input_dict['resize']:
|
||||
flavor_ref = input_dict['resize']['flavorRef']
|
||||
flavor_id = common.get_id_from_href(flavor_ref)
|
||||
self.compute_api.resize(req.environ['nova.context'], id,
|
||||
flavor_id)
|
||||
else:
|
||||
LOG.exception(_("Missing 'flavorRef' argument for resize"))
|
||||
raise exc.HTTPUnprocessableEntity()
|
||||
except Exception, e:
|
||||
LOG.exception(_("Error in resize %s"), e)
|
||||
raise exc.HTTPBadRequest()
|
||||
return webob.Response(status_int=202)
|
||||
flavor_ref = input_dict["resize"]["flavorRef"]
|
||||
except (KeyError, TypeError):
|
||||
msg = _("Resize requests require 'flavorRef' attribute.")
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
return self.resize(req, id, flavor_ref)
|
||||
|
||||
def _action_rebuild(self, info, request, instance_id):
|
||||
context = request.environ['nova.context']
|
||||
@@ -585,6 +680,48 @@ class ControllerV11(Controller):
|
||||
|
||||
return webob.Response(status_int=202)
|
||||
|
||||
def _action_create_image(self, input_dict, req, instance_id):
|
||||
"""Snapshot a server instance."""
|
||||
entity = input_dict.get("createImage", {})
|
||||
|
||||
try:
|
||||
image_name = entity["name"]
|
||||
|
||||
except KeyError:
|
||||
msg = _("createImage entity requires name attribute")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
except TypeError:
|
||||
msg = _("Malformed createImage entity")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
# preserve link to server in image properties
|
||||
server_ref = os.path.join(req.application_url,
|
||||
'servers',
|
||||
str(instance_id))
|
||||
props = {'instance_ref': server_ref}
|
||||
|
||||
metadata = entity.get('metadata', {})
|
||||
try:
|
||||
props.update(metadata)
|
||||
except ValueError:
|
||||
msg = _("Invalid metadata")
|
||||
raise webob.exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
context = req.environ['nova.context']
|
||||
image = self.compute_api.snapshot(context,
|
||||
instance_id,
|
||||
image_name,
|
||||
extra_properties=props)
|
||||
|
||||
# build location of newly-created image entity
|
||||
image_id = str(image['id'])
|
||||
image_ref = os.path.join(req.application_url, 'images', image_id)
|
||||
|
||||
resp = webob.Response(status_int=202)
|
||||
resp.headers['Location'] = image_ref
|
||||
return resp
|
||||
|
||||
def get_default_xmlns(self, req):
|
||||
return common.XML_NS_V11
|
||||
|
||||
@@ -599,6 +736,123 @@ class HeadersSerializer(wsgi.ResponseHeadersSerializer):
|
||||
response.status_int = 204
|
||||
|
||||
|
||||
class ServerXMLSerializer(wsgi.XMLDictSerializer):
|
||||
|
||||
xmlns = wsgi.XMLNS_V11
|
||||
|
||||
def __init__(self):
|
||||
self.metadata_serializer = common.MetadataXMLSerializer()
|
||||
self.addresses_serializer = ips.IPXMLSerializer()
|
||||
|
||||
def _create_basic_entity_node(self, xml_doc, id, links, name):
|
||||
basic_node = xml_doc.createElement(name)
|
||||
basic_node.setAttribute('id', str(id))
|
||||
link_nodes = self._create_link_nodes(xml_doc, links)
|
||||
for link_node in link_nodes:
|
||||
basic_node.appendChild(link_node)
|
||||
return basic_node
|
||||
|
||||
def _create_metadata_node(self, xml_doc, metadata):
|
||||
return self.metadata_serializer.meta_list_to_xml(xml_doc, metadata)
|
||||
|
||||
def _create_addresses_node(self, xml_doc, addresses):
|
||||
return self.addresses_serializer.networks_to_xml(xml_doc, addresses)
|
||||
|
||||
def _add_server_attributes(self, node, server):
|
||||
node.setAttribute('id', str(server['id']))
|
||||
node.setAttribute('uuid', str(server['uuid']))
|
||||
node.setAttribute('hostId', str(server['hostId']))
|
||||
node.setAttribute('name', server['name'])
|
||||
node.setAttribute('created', str(server['created']))
|
||||
node.setAttribute('updated', str(server['updated']))
|
||||
node.setAttribute('status', server['status'])
|
||||
if 'progress' in server:
|
||||
node.setAttribute('progress', str(server['progress']))
|
||||
|
||||
def _server_to_xml(self, xml_doc, server):
|
||||
server_node = xml_doc.createElement('server')
|
||||
server_node.setAttribute('id', str(server['id']))
|
||||
server_node.setAttribute('name', server['name'])
|
||||
link_nodes = self._create_link_nodes(xml_doc,
|
||||
server['links'])
|
||||
for link_node in link_nodes:
|
||||
server_node.appendChild(link_node)
|
||||
return server_node
|
||||
|
||||
def _server_to_xml_detailed(self, xml_doc, server):
|
||||
server_node = xml_doc.createElement('server')
|
||||
self._add_server_attributes(server_node, server)
|
||||
|
||||
link_nodes = self._create_link_nodes(xml_doc,
|
||||
server['links'])
|
||||
for link_node in link_nodes:
|
||||
server_node.appendChild(link_node)
|
||||
|
||||
if 'image' in server:
|
||||
image_node = self._create_basic_entity_node(xml_doc,
|
||||
server['image']['id'],
|
||||
server['image']['links'],
|
||||
'image')
|
||||
server_node.appendChild(image_node)
|
||||
|
||||
if 'flavor' in server:
|
||||
flavor_node = self._create_basic_entity_node(xml_doc,
|
||||
server['flavor']['id'],
|
||||
server['flavor']['links'],
|
||||
'flavor')
|
||||
server_node.appendChild(flavor_node)
|
||||
|
||||
metadata = server.get('metadata', {}).items()
|
||||
if len(metadata) > 0:
|
||||
metadata_node = self._create_metadata_node(xml_doc, metadata)
|
||||
server_node.appendChild(metadata_node)
|
||||
|
||||
addresses_node = self._create_addresses_node(xml_doc,
|
||||
server['addresses'])
|
||||
server_node.appendChild(addresses_node)
|
||||
|
||||
return server_node
|
||||
|
||||
def _server_list_to_xml(self, xml_doc, servers, detailed):
|
||||
container_node = xml_doc.createElement('servers')
|
||||
if detailed:
|
||||
server_to_xml = self._server_to_xml_detailed
|
||||
else:
|
||||
server_to_xml = self._server_to_xml
|
||||
|
||||
for server in servers:
|
||||
item_node = server_to_xml(xml_doc, server)
|
||||
container_node.appendChild(item_node)
|
||||
return container_node
|
||||
|
||||
def index(self, servers_dict):
|
||||
xml_doc = minidom.Document()
|
||||
node = self._server_list_to_xml(xml_doc,
|
||||
servers_dict['servers'],
|
||||
detailed=False)
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
def detail(self, servers_dict):
|
||||
xml_doc = minidom.Document()
|
||||
node = self._server_list_to_xml(xml_doc,
|
||||
servers_dict['servers'],
|
||||
detailed=True)
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
def show(self, server_dict):
|
||||
xml_doc = minidom.Document()
|
||||
node = self._server_to_xml_detailed(xml_doc,
|
||||
server_dict['server'])
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
def create(self, server_dict):
|
||||
xml_doc = minidom.Document()
|
||||
node = self._server_to_xml_detailed(xml_doc,
|
||||
server_dict['server'])
|
||||
node.setAttribute('adminPass', server_dict['server']['adminPass'])
|
||||
return self.to_xml_string(node, True)
|
||||
|
||||
|
||||
def create_resource(version='1.0'):
|
||||
controller = {
|
||||
'1.0': ControllerV10,
|
||||
@@ -628,9 +882,13 @@ def create_resource(version='1.0'):
|
||||
|
||||
headers_serializer = HeadersSerializer()
|
||||
|
||||
xml_serializer = {
|
||||
'1.0': wsgi.XMLDictSerializer(metadata, wsgi.XMLNS_V10),
|
||||
'1.1': ServerXMLSerializer(),
|
||||
}[version]
|
||||
|
||||
body_serializers = {
|
||||
'application/xml': wsgi.XMLDictSerializer(metadata=metadata,
|
||||
xmlns=xmlns),
|
||||
'application/xml': xml_serializer,
|
||||
}
|
||||
|
||||
body_deserializers = {
|
||||
|
||||
+234
-36
@@ -24,7 +24,66 @@ import nova.api.openstack.views.versions
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
ATOM_XMLNS = "http://www.w3.org/2005/Atom"
|
||||
VERSIONS = {
|
||||
"v1.0": {
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.0+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
"v1.1": {
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.1+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Versions(wsgi.Resource):
|
||||
@@ -36,16 +95,20 @@ class Versions(wsgi.Resource):
|
||||
}
|
||||
}
|
||||
|
||||
headers_serializer = VersionsHeadersSerializer()
|
||||
|
||||
body_serializers = {
|
||||
'application/atom+xml': VersionsAtomSerializer(metadata=metadata),
|
||||
'application/xml': VersionsXMLSerializer(metadata=metadata),
|
||||
}
|
||||
serializer = wsgi.ResponseSerializer(body_serializers)
|
||||
serializer = wsgi.ResponseSerializer(
|
||||
body_serializers=body_serializers,
|
||||
headers_serializer=headers_serializer)
|
||||
|
||||
supported_content_types = ('application/json',
|
||||
'application/xml',
|
||||
'application/atom+xml')
|
||||
deserializer = wsgi.RequestDeserializer(
|
||||
deserializer = VersionsRequestDeserializer(
|
||||
supported_content_types=supported_content_types)
|
||||
|
||||
wsgi.Resource.__init__(self, None, serializer=serializer,
|
||||
@@ -53,60 +116,131 @@ class Versions(wsgi.Resource):
|
||||
|
||||
def dispatch(self, request, *args):
|
||||
"""Respond to a request for all OpenStack API versions."""
|
||||
version_objs = [
|
||||
{
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
#TODO(wwolf) get correct value for these
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
},
|
||||
{
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
#TODO(wwolf) get correct value for these
|
||||
"updated": "2010-10-09T11:30:00Z",
|
||||
},
|
||||
]
|
||||
|
||||
builder = nova.api.openstack.views.versions.get_view_builder(request)
|
||||
versions = [builder.build(version) for version in version_objs]
|
||||
return dict(versions=versions)
|
||||
if request.path == '/':
|
||||
# List Versions
|
||||
return builder.build_versions(VERSIONS)
|
||||
else:
|
||||
# Versions Multiple Choice
|
||||
return builder.build_choices(VERSIONS, request)
|
||||
|
||||
|
||||
class VersionV10(object):
|
||||
def show(self, req):
|
||||
builder = nova.api.openstack.views.versions.get_view_builder(req)
|
||||
return builder.build_version(VERSIONS['v1.0'])
|
||||
|
||||
|
||||
class VersionV11(object):
|
||||
def show(self, req):
|
||||
builder = nova.api.openstack.views.versions.get_view_builder(req)
|
||||
return builder.build_version(VERSIONS['v1.1'])
|
||||
|
||||
|
||||
class VersionsRequestDeserializer(wsgi.RequestDeserializer):
|
||||
def get_expected_content_type(self, request):
|
||||
supported_content_types = list(self.supported_content_types)
|
||||
if request.path != '/':
|
||||
# Remove atom+xml accept type for 300 responses
|
||||
if 'application/atom+xml' in supported_content_types:
|
||||
supported_content_types.remove('application/atom+xml')
|
||||
|
||||
return request.best_match_content_type(supported_content_types)
|
||||
|
||||
def get_action_args(self, request_environment):
|
||||
"""Parse dictionary created by routes library."""
|
||||
args = {}
|
||||
if request_environment['PATH_INFO'] == '/':
|
||||
args['action'] = 'index'
|
||||
else:
|
||||
args['action'] = 'multi'
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class VersionsXMLSerializer(wsgi.XMLDictSerializer):
|
||||
def _versions_to_xml(self, versions):
|
||||
root = self._xml_doc.createElement('versions')
|
||||
#TODO(wwolf): this is temporary until we get rid of toprettyxml
|
||||
# in the base class (XMLDictSerializer), which I plan to do in
|
||||
# another branch
|
||||
def to_xml_string(self, node, has_atom=False):
|
||||
self._add_xmlns(node, has_atom)
|
||||
return node.toxml(encoding='UTF-8')
|
||||
|
||||
def _versions_to_xml(self, versions, name="versions", xmlns=None):
|
||||
root = self._xml_doc.createElement(name)
|
||||
root.setAttribute("xmlns", wsgi.XMLNS_V11)
|
||||
root.setAttribute("xmlns:atom", wsgi.XMLNS_ATOM)
|
||||
|
||||
for version in versions:
|
||||
root.appendChild(self._create_version_node(version))
|
||||
|
||||
return root
|
||||
|
||||
def _create_version_node(self, version):
|
||||
def _create_media_types(self, media_types):
|
||||
base = self._xml_doc.createElement('media-types')
|
||||
for type in media_types:
|
||||
node = self._xml_doc.createElement('media-type')
|
||||
node.setAttribute('base', type['base'])
|
||||
node.setAttribute('type', type['type'])
|
||||
base.appendChild(node)
|
||||
|
||||
return base
|
||||
|
||||
def _create_version_node(self, version, create_ns=False):
|
||||
version_node = self._xml_doc.createElement('version')
|
||||
if create_ns:
|
||||
xmlns = wsgi.XMLNS_V11
|
||||
xmlns_atom = wsgi.XMLNS_ATOM
|
||||
version_node.setAttribute('xmlns', xmlns)
|
||||
version_node.setAttribute('xmlns:atom', xmlns_atom)
|
||||
|
||||
version_node.setAttribute('id', version['id'])
|
||||
version_node.setAttribute('status', version['status'])
|
||||
version_node.setAttribute('updated', version['updated'])
|
||||
if 'updated' in version:
|
||||
version_node.setAttribute('updated', version['updated'])
|
||||
|
||||
for link in version['links']:
|
||||
link_node = self._xml_doc.createElement('atom:link')
|
||||
link_node.setAttribute('rel', link['rel'])
|
||||
link_node.setAttribute('href', link['href'])
|
||||
version_node.appendChild(link_node)
|
||||
if 'media-types' in version:
|
||||
media_types = self._create_media_types(version['media-types'])
|
||||
version_node.appendChild(media_types)
|
||||
|
||||
link_nodes = self._create_link_nodes(self._xml_doc, version['links'])
|
||||
for link in link_nodes:
|
||||
version_node.appendChild(link)
|
||||
|
||||
return version_node
|
||||
|
||||
def default(self, data):
|
||||
def index(self, data):
|
||||
self._xml_doc = minidom.Document()
|
||||
node = self._versions_to_xml(data['versions'])
|
||||
|
||||
return self.to_xml_string(node)
|
||||
|
||||
def show(self, data):
|
||||
self._xml_doc = minidom.Document()
|
||||
node = self._create_version_node(data['version'], True)
|
||||
|
||||
return self.to_xml_string(node)
|
||||
|
||||
def multi(self, data):
|
||||
self._xml_doc = minidom.Document()
|
||||
node = self._versions_to_xml(data['choices'], 'choices',
|
||||
xmlns=wsgi.XMLNS_V11)
|
||||
|
||||
return self.to_xml_string(node)
|
||||
|
||||
|
||||
class VersionsAtomSerializer(wsgi.XMLDictSerializer):
|
||||
#TODO(wwolf): this is temporary until we get rid of toprettyxml
|
||||
# in the base class (XMLDictSerializer), which I plan to do in
|
||||
# another branch
|
||||
def to_xml_string(self, node, has_atom=False):
|
||||
self._add_xmlns(node, has_atom)
|
||||
return node.toxml(encoding='UTF-8')
|
||||
|
||||
def __init__(self, metadata=None, xmlns=None):
|
||||
self.metadata = metadata or {}
|
||||
if not xmlns:
|
||||
self.xmlns = ATOM_XMLNS
|
||||
self.xmlns = wsgi.XMLNS_ATOM
|
||||
else:
|
||||
self.xmlns = xmlns
|
||||
|
||||
@@ -135,8 +269,33 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
|
||||
link_href = link_href.rstrip('/')
|
||||
return link_href.rsplit('/', 1)[0] + '/'
|
||||
|
||||
def _create_meta(self, root, versions):
|
||||
title = self._create_text_elem('title', 'Available API Versions',
|
||||
def _create_detail_meta(self, root, version):
|
||||
title = self._create_text_elem('title', "About This Version",
|
||||
type='text')
|
||||
|
||||
updated = self._create_text_elem('updated', version['updated'])
|
||||
|
||||
uri = version['links'][0]['href']
|
||||
id = self._create_text_elem('id', uri)
|
||||
|
||||
link = self._xml_doc.createElement('link')
|
||||
link.setAttribute('rel', 'self')
|
||||
link.setAttribute('href', uri)
|
||||
|
||||
author = self._xml_doc.createElement('author')
|
||||
author_name = self._create_text_elem('name', 'Rackspace')
|
||||
author_uri = self._create_text_elem('uri', 'http://www.rackspace.com/')
|
||||
author.appendChild(author_name)
|
||||
author.appendChild(author_uri)
|
||||
|
||||
root.appendChild(title)
|
||||
root.appendChild(updated)
|
||||
root.appendChild(id)
|
||||
root.appendChild(author)
|
||||
root.appendChild(link)
|
||||
|
||||
def _create_list_meta(self, root, versions):
|
||||
title = self._create_text_elem('title', "Available API Versions",
|
||||
type='text')
|
||||
# Set this updated to the most recently updated version
|
||||
recent = self._get_most_recent_update(versions)
|
||||
@@ -144,6 +303,7 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
|
||||
|
||||
base_url = self._get_base_url(versions[0]['links'][0]['href'])
|
||||
id = self._create_text_elem('id', base_url)
|
||||
|
||||
link = self._xml_doc.createElement('link')
|
||||
link.setAttribute('rel', 'self')
|
||||
link.setAttribute('href', base_url)
|
||||
@@ -178,7 +338,10 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
|
||||
link_node = self._xml_doc.createElement('link')
|
||||
link_node.setAttribute('rel', link['rel'])
|
||||
link_node.setAttribute('href', link['href'])
|
||||
entry.appendChild(link_node)
|
||||
if 'type' in link:
|
||||
link_node.setAttribute('type', link['type'])
|
||||
|
||||
entry.appendChild(link_node)
|
||||
|
||||
content = self._create_text_elem('content',
|
||||
'Version %s %s (%s)' %
|
||||
@@ -190,10 +353,45 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
|
||||
entry.appendChild(content)
|
||||
root.appendChild(entry)
|
||||
|
||||
def default(self, data):
|
||||
def index(self, data):
|
||||
self._xml_doc = minidom.Document()
|
||||
node = self._xml_doc.createElementNS(self.xmlns, 'feed')
|
||||
self._create_meta(node, data['versions'])
|
||||
self._create_list_meta(node, data['versions'])
|
||||
self._create_version_entries(node, data['versions'])
|
||||
|
||||
return self.to_xml_string(node)
|
||||
|
||||
def show(self, data):
|
||||
self._xml_doc = minidom.Document()
|
||||
node = self._xml_doc.createElementNS(self.xmlns, 'feed')
|
||||
self._create_detail_meta(node, data['version'])
|
||||
self._create_version_entries(node, [data['version']])
|
||||
|
||||
return self.to_xml_string(node)
|
||||
|
||||
|
||||
class VersionsHeadersSerializer(wsgi.ResponseHeadersSerializer):
|
||||
def multi(self, response, data):
|
||||
response.status_int = 300
|
||||
|
||||
|
||||
def create_resource(version='1.0'):
|
||||
controller = {
|
||||
'1.0': VersionV10,
|
||||
'1.1': VersionV11,
|
||||
}[version]()
|
||||
|
||||
body_serializers = {
|
||||
'application/xml': VersionsXMLSerializer(),
|
||||
'application/atom+xml': VersionsAtomSerializer(),
|
||||
}
|
||||
serializer = wsgi.ResponseSerializer(body_serializers)
|
||||
|
||||
supported_content_types = ('application/json',
|
||||
'application/xml',
|
||||
'application/atom+xml')
|
||||
deserializer = wsgi.RequestDeserializer(
|
||||
supported_content_types=supported_content_types)
|
||||
|
||||
return wsgi.Resource(controller, serializer=serializer,
|
||||
deserializer=deserializer)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
@@ -149,8 +150,8 @@ class ViewBuilderV11(ViewBuilder):
|
||||
|
||||
def _build_detail(self, inst):
|
||||
response = super(ViewBuilderV11, self)._build_detail(inst)
|
||||
response['server']['created'] = inst['created_at']
|
||||
response['server']['updated'] = inst['updated_at']
|
||||
response['server']['created'] = utils.isotime(inst['created_at'])
|
||||
response['server']['updated'] = utils.isotime(inst['updated_at'])
|
||||
if 'status' in response['server']:
|
||||
if response['server']['status'] == "ACTIVE":
|
||||
response['server']['progress'] = 100
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import os
|
||||
|
||||
|
||||
@@ -31,16 +32,44 @@ class ViewBuilder(object):
|
||||
"""
|
||||
self.base_url = base_url
|
||||
|
||||
def build(self, version_data):
|
||||
"""Generic method used to generate a version entity."""
|
||||
version = {
|
||||
"id": version_data["id"],
|
||||
"status": version_data["status"],
|
||||
"updated": version_data["updated"],
|
||||
"links": self._build_links(version_data),
|
||||
}
|
||||
def build_choices(self, VERSIONS, req):
|
||||
version_objs = []
|
||||
for version in VERSIONS:
|
||||
version = VERSIONS[version]
|
||||
version_objs.append({
|
||||
"id": version['id'],
|
||||
"status": version['status'],
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": self.generate_href(version['id'], req.path)
|
||||
}
|
||||
],
|
||||
"media-types": version['media-types']
|
||||
})
|
||||
|
||||
return version
|
||||
return dict(choices=version_objs)
|
||||
|
||||
def build_versions(self, versions):
|
||||
version_objs = []
|
||||
for version in versions:
|
||||
version = versions[version]
|
||||
version_objs.append({
|
||||
"id": version['id'],
|
||||
"status": version['status'],
|
||||
"updated": version['updated'],
|
||||
"links": self._build_links(version),
|
||||
})
|
||||
|
||||
return dict(versions=version_objs)
|
||||
|
||||
def build_version(self, version):
|
||||
reval = copy.deepcopy(version)
|
||||
reval['links'].insert(0, {
|
||||
"rel": "self",
|
||||
"href": self.base_url.rstrip('/') + '/',
|
||||
})
|
||||
return dict(version=reval)
|
||||
|
||||
def _build_links(self, version_data):
|
||||
"""Generate a container of links that refer to the provided version."""
|
||||
@@ -55,6 +84,11 @@ class ViewBuilder(object):
|
||||
|
||||
return links
|
||||
|
||||
def generate_href(self, version_number):
|
||||
def generate_href(self, version_number, path=None):
|
||||
"""Create an url that refers to a specific version_number."""
|
||||
return os.path.join(self.base_url, version_number) + '/'
|
||||
version_number = version_number.strip('/')
|
||||
if path:
|
||||
path = path.strip('/')
|
||||
return os.path.join(self.base_url, version_number, path)
|
||||
else:
|
||||
return os.path.join(self.base_url, version_number) + '/'
|
||||
|
||||
@@ -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')
|
||||
@@ -165,12 +166,11 @@ 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)
|
||||
if metadata_node is not None:
|
||||
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
|
||||
|
||||
|
||||
@@ -387,6 +387,8 @@ class XMLDictSerializer(DictSerializer):
|
||||
link_node = xml_doc.createElement('atom:link')
|
||||
link_node.setAttribute('rel', link['rel'])
|
||||
link_node.setAttribute('href', link['href'])
|
||||
if 'type' in link:
|
||||
link_node.setAttribute('type', link['type'])
|
||||
link_nodes.append(link_node)
|
||||
return link_nodes
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ class DbDriver(object):
|
||||
|
||||
try:
|
||||
project = db.project_create(context.get_admin_context(), values)
|
||||
except exception.Duplicate:
|
||||
except exception.DBError:
|
||||
raise exception.ProjectExists(project=name)
|
||||
|
||||
for member in members:
|
||||
|
||||
@@ -518,6 +518,14 @@ class AuthManager(object):
|
||||
return drv.get_user_roles(User.safe_id(user),
|
||||
Project.safe_id(project))
|
||||
|
||||
def get_active_roles(self, user, project=None):
|
||||
"""Get all active roles for context"""
|
||||
if project:
|
||||
roles = FLAGS.allowed_roles + ['projectmanager']
|
||||
else:
|
||||
roles = FLAGS.global_roles
|
||||
return [role for role in roles if self.has_role(user, role, project)]
|
||||
|
||||
def get_project(self, pid):
|
||||
"""Get project object by id"""
|
||||
with self.driver() as drv:
|
||||
@@ -730,10 +738,6 @@ class AuthManager(object):
|
||||
with self.driver() as drv:
|
||||
drv.modify_user(uid, access_key, secret_key, admin)
|
||||
|
||||
@staticmethod
|
||||
def get_key_pairs(context):
|
||||
return db.key_pair_get_all_by_user(context.elevated(), context.user_id)
|
||||
|
||||
def get_credentials(self, user, project=None, use_dmz=True):
|
||||
"""Get credential zip for user in project"""
|
||||
if not isinstance(user, User):
|
||||
@@ -785,7 +789,7 @@ class AuthManager(object):
|
||||
return read_buffer
|
||||
|
||||
def get_environment_rc(self, user, project=None, use_dmz=True):
|
||||
"""Get credential zip for user in project"""
|
||||
"""Get environment rc for user in project"""
|
||||
if not isinstance(user, User):
|
||||
user = self.get_user(user)
|
||||
if project is None:
|
||||
|
||||
@@ -96,8 +96,8 @@ class CloudPipe(object):
|
||||
def launch_vpn_instance(self, project_id):
|
||||
LOG.debug(_("Launching VPN for %s") % (project_id))
|
||||
project = self.manager.get_project(project_id)
|
||||
ctxt = context.RequestContext(user=project.project_manager,
|
||||
project=project)
|
||||
ctxt = context.RequestContext(user=project.project_manager_id,
|
||||
project=project.id)
|
||||
key_name = self.setup_key_pair(ctxt)
|
||||
group_name = self.setup_security_group(ctxt)
|
||||
|
||||
@@ -112,11 +112,11 @@ class CloudPipe(object):
|
||||
security_group=[group_name])
|
||||
|
||||
def setup_security_group(self, context):
|
||||
group_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix)
|
||||
if db.security_group_exists(context, context.project.id, group_name):
|
||||
group_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix)
|
||||
if db.security_group_exists(context, context.project_id, group_name):
|
||||
return group_name
|
||||
group = {'user_id': context.user.id,
|
||||
'project_id': context.project.id,
|
||||
group = {'user_id': context.user_id,
|
||||
'project_id': context.project_id,
|
||||
'name': group_name,
|
||||
'description': 'Group for vpn'}
|
||||
group_ref = db.security_group_create(context, group)
|
||||
@@ -137,12 +137,12 @@ class CloudPipe(object):
|
||||
return group_name
|
||||
|
||||
def setup_key_pair(self, context):
|
||||
key_name = '%s%s' % (context.project.id, FLAGS.vpn_key_suffix)
|
||||
key_name = '%s%s' % (context.project_id, FLAGS.vpn_key_suffix)
|
||||
try:
|
||||
result = cloud._gen_key(context, context.user.id, key_name)
|
||||
result = cloud._gen_key(context, context.user_id, key_name)
|
||||
private_key = result['private_key']
|
||||
try:
|
||||
key_dir = os.path.join(FLAGS.keys_path, context.user.id)
|
||||
key_dir = os.path.join(FLAGS.keys_path, context.user_id)
|
||||
if not os.path.exists(key_dir):
|
||||
os.makedirs(key_dir)
|
||||
key_path = os.path.join(key_dir, '%s.pem' % key_name)
|
||||
|
||||
+4
-7
@@ -689,7 +689,7 @@ class API(base.Base):
|
||||
raise
|
||||
instances = None
|
||||
elif project_id or not context.is_admin:
|
||||
if not context.project:
|
||||
if not context.project_id:
|
||||
instances = self.db.instance_get_all_by_user(
|
||||
context, context.user_id)
|
||||
else:
|
||||
@@ -940,18 +940,15 @@ class API(base.Base):
|
||||
LOG.debug(_("Old instance type %(current_instance_type_name)s, "
|
||||
" new instance type %(new_instance_type_name)s") % locals())
|
||||
if not new_instance_type:
|
||||
raise exception.ApiError(_("Requested flavor %(flavor_id)d "
|
||||
"does not exist") % locals())
|
||||
raise exception.FlavorNotFound(flavor_id=flavor_id)
|
||||
|
||||
current_memory_mb = current_instance_type['memory_mb']
|
||||
new_memory_mb = new_instance_type['memory_mb']
|
||||
if current_memory_mb > new_memory_mb:
|
||||
raise exception.ApiError(_("Invalid flavor: cannot downsize"
|
||||
"instances"))
|
||||
raise exception.CannotResizeToSmallerSize()
|
||||
|
||||
if (current_memory_mb == new_memory_mb) and flavor_id:
|
||||
raise exception.ApiError(_("Invalid flavor: cannot use"
|
||||
"the same flavor. "))
|
||||
raise exception.CannotResizeToSameSize()
|
||||
|
||||
instance_ref = self._get_instance(context, instance_id, 'resize')
|
||||
self._cast_scheduler_message(context,
|
||||
|
||||
@@ -132,11 +132,8 @@ def get_instance_type_by_name(name):
|
||||
# flavors.
|
||||
def get_instance_type_by_flavor_id(flavor_id):
|
||||
"""Retrieve instance type by flavor_id."""
|
||||
if flavor_id is None:
|
||||
return get_default_instance_type()
|
||||
ctxt = context.get_admin_context()
|
||||
try:
|
||||
ctxt = context.get_admin_context()
|
||||
return db.instance_type_get_by_flavor_id(ctxt, flavor_id)
|
||||
except exception.DBError, e:
|
||||
LOG.exception(_('DB error: %s') % e)
|
||||
raise exception.ApiError(_("Unknown flavor: %s") % flavor_id)
|
||||
except ValueError:
|
||||
raise exception.FlavorNotFound(flavor_id=flavor_id)
|
||||
|
||||
+19
-13
@@ -326,7 +326,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
self._update_state(context, instance_id, power_state.BUILDING)
|
||||
|
||||
try:
|
||||
self.driver.spawn(instance, network_info, bd_mapping)
|
||||
self.driver.spawn(context, instance, network_info, bd_mapping)
|
||||
except Exception as ex: # pylint: disable=W0702
|
||||
msg = _("Instance '%(instance_id)s' failed to spawn. Is "
|
||||
"virtualization enabled in the BIOS? Details: "
|
||||
@@ -433,7 +433,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
network_info = self.network_api.get_instance_nw_info(context,
|
||||
instance_ref)
|
||||
bd_mapping = self._setup_block_device_mapping(context, instance_id)
|
||||
self.driver.spawn(instance_ref, network_info, bd_mapping)
|
||||
self.driver.spawn(context, instance_ref, network_info, bd_mapping)
|
||||
|
||||
self._update_image_ref(context, instance_id, image_ref)
|
||||
self._update_launched_at(context, instance_id)
|
||||
@@ -501,7 +501,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
'instance: %(instance_id)s (state: %(state)s '
|
||||
'expected: %(running)s)') % locals())
|
||||
|
||||
self.driver.snapshot(instance_ref, image_id)
|
||||
self.driver.snapshot(context, instance_ref, image_id)
|
||||
|
||||
if image_type == 'snapshot':
|
||||
if rotation:
|
||||
@@ -660,7 +660,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
_update_state = lambda result: self._update_state_callback(
|
||||
self, context, instance_id, result)
|
||||
network_info = self._get_instance_nw_info(context, instance_ref)
|
||||
self.driver.rescue(instance_ref, _update_state, network_info)
|
||||
self.driver.rescue(context, instance_ref, _update_state, network_info)
|
||||
self._update_state(context, instance_id)
|
||||
|
||||
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
|
||||
@@ -747,7 +747,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
local_gb=instance_type['local_gb'],
|
||||
instance_type_id=instance_type['id']))
|
||||
|
||||
self.driver.revert_resize(instance_ref)
|
||||
self.driver.revert_migration(instance_ref)
|
||||
self.db.migration_update(context, migration_id,
|
||||
{'status': 'reverted'})
|
||||
usage_info = utils.usage_from_instance(instance_ref)
|
||||
@@ -845,20 +845,26 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
|
||||
"""
|
||||
migration_ref = self.db.migration_get(context, migration_id)
|
||||
|
||||
resize_instance = False
|
||||
instance_ref = self.db.instance_get_by_uuid(context,
|
||||
migration_ref.instance_uuid)
|
||||
instance_type = self.db.instance_type_get_by_flavor_id(context,
|
||||
migration_ref['new_flavor_id'])
|
||||
self.db.instance_update(context, instance_ref.uuid,
|
||||
dict(instance_type_id=instance_type['id'],
|
||||
memory_mb=instance_type['memory_mb'],
|
||||
vcpus=instance_type['vcpus'],
|
||||
local_gb=instance_type['local_gb']))
|
||||
if migration_ref['old_flavor_id'] != migration_ref['new_flavor_id']:
|
||||
instance_type = self.db.instance_type_get_by_flavor_id(context,
|
||||
migration_ref['new_flavor_id'])
|
||||
self.db.instance_update(context, instance_ref.uuid,
|
||||
dict(instance_type_id=instance_type['id'],
|
||||
memory_mb=instance_type['memory_mb'],
|
||||
vcpus=instance_type['vcpus'],
|
||||
local_gb=instance_type['local_gb']))
|
||||
resize_instance = True
|
||||
|
||||
instance_ref = self.db.instance_get_by_uuid(context,
|
||||
instance_ref.uuid)
|
||||
|
||||
network_info = self._get_instance_nw_info(context, instance_ref)
|
||||
self.driver.finish_resize(instance_ref, disk_info, network_info)
|
||||
self.driver.finish_migration(context, instance_ref, disk_info,
|
||||
network_info, resize_instance)
|
||||
|
||||
self.db.migration_update(context, migration_id,
|
||||
{'status': 'finished', })
|
||||
|
||||
@@ -1,435 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Instance Monitoring:
|
||||
|
||||
Optionally may be run on each compute node. Provides RRD
|
||||
based statistics and graphs and makes them internally available
|
||||
in the object store.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
|
||||
import boto
|
||||
import boto.s3
|
||||
import rrdtool
|
||||
from twisted.internet import task
|
||||
from twisted.application import service
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
from nova.virt import connection as virt_connection
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('monitoring_instances_delay', 5,
|
||||
'Sleep time between updates')
|
||||
flags.DEFINE_integer('monitoring_instances_step', 300,
|
||||
'Interval of RRD updates')
|
||||
flags.DEFINE_string('monitoring_rrd_path', '$state_path/monitor/instances',
|
||||
'Location of RRD files')
|
||||
|
||||
|
||||
RRD_VALUES = {
|
||||
'cpu': [
|
||||
'DS:cpu:GAUGE:600:0:100',
|
||||
'RRA:AVERAGE:0.5:1:800',
|
||||
'RRA:AVERAGE:0.5:6:800',
|
||||
'RRA:AVERAGE:0.5:24:800',
|
||||
'RRA:AVERAGE:0.5:288:800',
|
||||
'RRA:MAX:0.5:1:800',
|
||||
'RRA:MAX:0.5:6:800',
|
||||
'RRA:MAX:0.5:24:800',
|
||||
'RRA:MAX:0.5:288:800',
|
||||
],
|
||||
'net': [
|
||||
'DS:rx:COUNTER:600:0:1250000',
|
||||
'DS:tx:COUNTER:600:0:1250000',
|
||||
'RRA:AVERAGE:0.5:1:800',
|
||||
'RRA:AVERAGE:0.5:6:800',
|
||||
'RRA:AVERAGE:0.5:24:800',
|
||||
'RRA:AVERAGE:0.5:288:800',
|
||||
'RRA:MAX:0.5:1:800',
|
||||
'RRA:MAX:0.5:6:800',
|
||||
'RRA:MAX:0.5:24:800',
|
||||
'RRA:MAX:0.5:288:800',
|
||||
],
|
||||
'disk': [
|
||||
'DS:rd:COUNTER:600:U:U',
|
||||
'DS:wr:COUNTER:600:U:U',
|
||||
'RRA:AVERAGE:0.5:1:800',
|
||||
'RRA:AVERAGE:0.5:6:800',
|
||||
'RRA:AVERAGE:0.5:24:800',
|
||||
'RRA:AVERAGE:0.5:288:800',
|
||||
'RRA:MAX:0.5:1:800',
|
||||
'RRA:MAX:0.5:6:800',
|
||||
'RRA:MAX:0.5:24:800',
|
||||
'RRA:MAX:0.5:444:800',
|
||||
]}
|
||||
|
||||
|
||||
utcnow = utils.utcnow
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.compute.monitor')
|
||||
|
||||
|
||||
def update_rrd(instance, name, data):
|
||||
"""
|
||||
Updates the specified RRD file.
|
||||
"""
|
||||
filename = os.path.join(instance.get_rrd_path(), '%s.rrd' % name)
|
||||
|
||||
if not os.path.exists(filename):
|
||||
init_rrd(instance, name)
|
||||
|
||||
timestamp = int(time.mktime(utcnow().timetuple()))
|
||||
rrdtool.update(filename, '%d:%s' % (timestamp, data))
|
||||
|
||||
|
||||
def init_rrd(instance, name):
|
||||
"""
|
||||
Initializes the specified RRD file.
|
||||
"""
|
||||
path = os.path.join(FLAGS.monitoring_rrd_path, instance.instance_id)
|
||||
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
filename = os.path.join(path, '%s.rrd' % name)
|
||||
|
||||
if not os.path.exists(filename):
|
||||
rrdtool.create(
|
||||
filename,
|
||||
'--step', '%d' % FLAGS.monitoring_instances_step,
|
||||
'--start', '0',
|
||||
*RRD_VALUES[name])
|
||||
|
||||
|
||||
def graph_cpu(instance, duration):
|
||||
"""
|
||||
Creates a graph of cpu usage for the specified instance and duration.
|
||||
"""
|
||||
path = instance.get_rrd_path()
|
||||
filename = os.path.join(path, 'cpu-%s.png' % duration)
|
||||
|
||||
rrdtool.graph(
|
||||
filename,
|
||||
'--disable-rrdtool-tag',
|
||||
'--imgformat', 'PNG',
|
||||
'--width', '400',
|
||||
'--height', '120',
|
||||
'--start', 'now-%s' % duration,
|
||||
'--vertical-label', '% cpu used',
|
||||
'-l', '0',
|
||||
'-u', '100',
|
||||
'DEF:cpu=%s:cpu:AVERAGE' % os.path.join(path, 'cpu.rrd'),
|
||||
'AREA:cpu#eacc00:% CPU',)
|
||||
|
||||
store_graph(instance.instance_id, filename)
|
||||
|
||||
|
||||
def graph_net(instance, duration):
|
||||
"""
|
||||
Creates a graph of network usage for the specified instance and duration.
|
||||
"""
|
||||
path = instance.get_rrd_path()
|
||||
filename = os.path.join(path, 'net-%s.png' % duration)
|
||||
|
||||
rrdtool.graph(
|
||||
filename,
|
||||
'--disable-rrdtool-tag',
|
||||
'--imgformat', 'PNG',
|
||||
'--width', '400',
|
||||
'--height', '120',
|
||||
'--start', 'now-%s' % duration,
|
||||
'--vertical-label', 'bytes/s',
|
||||
'--logarithmic',
|
||||
'--units', 'si',
|
||||
'--lower-limit', '1000',
|
||||
'--rigid',
|
||||
'DEF:rx=%s:rx:AVERAGE' % os.path.join(path, 'net.rrd'),
|
||||
'DEF:tx=%s:tx:AVERAGE' % os.path.join(path, 'net.rrd'),
|
||||
'AREA:rx#00FF00:In traffic',
|
||||
'LINE1:tx#0000FF:Out traffic',)
|
||||
|
||||
store_graph(instance.instance_id, filename)
|
||||
|
||||
|
||||
def graph_disk(instance, duration):
|
||||
"""
|
||||
Creates a graph of disk usage for the specified duration.
|
||||
"""
|
||||
path = instance.get_rrd_path()
|
||||
filename = os.path.join(path, 'disk-%s.png' % duration)
|
||||
|
||||
rrdtool.graph(
|
||||
filename,
|
||||
'--disable-rrdtool-tag',
|
||||
'--imgformat', 'PNG',
|
||||
'--width', '400',
|
||||
'--height', '120',
|
||||
'--start', 'now-%s' % duration,
|
||||
'--vertical-label', 'bytes/s',
|
||||
'--logarithmic',
|
||||
'--units', 'si',
|
||||
'--lower-limit', '1000',
|
||||
'--rigid',
|
||||
'DEF:rd=%s:rd:AVERAGE' % os.path.join(path, 'disk.rrd'),
|
||||
'DEF:wr=%s:wr:AVERAGE' % os.path.join(path, 'disk.rrd'),
|
||||
'AREA:rd#00FF00:Read',
|
||||
'LINE1:wr#0000FF:Write',)
|
||||
|
||||
store_graph(instance.instance_id, filename)
|
||||
|
||||
|
||||
def store_graph(instance_id, filename):
|
||||
"""
|
||||
Transmits the specified graph file to internal object store on cloud
|
||||
controller.
|
||||
"""
|
||||
# TODO(devcamcar): Need to use an asynchronous method to make this
|
||||
# connection. If boto has some separate method that generates
|
||||
# the request it would like to make and another method to parse
|
||||
# the response we can make our own client that does the actual
|
||||
# request and hands it off to the response parser.
|
||||
s3 = boto.s3.connection.S3Connection(
|
||||
aws_access_key_id=FLAGS.aws_access_key_id,
|
||||
aws_secret_access_key=FLAGS.aws_secret_access_key,
|
||||
is_secure=False,
|
||||
calling_format=boto.s3.connection.OrdinaryCallingFormat(),
|
||||
port=FLAGS.s3_port,
|
||||
host=FLAGS.s3_host)
|
||||
bucket_name = '_%s.monitor' % instance_id
|
||||
|
||||
# Object store isn't creating the bucket like it should currently
|
||||
# when it is first requested, so have to catch and create manually.
|
||||
try:
|
||||
bucket = s3.get_bucket(bucket_name)
|
||||
except Exception:
|
||||
bucket = s3.create_bucket(bucket_name)
|
||||
|
||||
key = boto.s3.Key(bucket)
|
||||
key.key = os.path.basename(filename)
|
||||
key.set_contents_from_filename(filename)
|
||||
|
||||
|
||||
class Instance(object):
|
||||
def __init__(self, conn, instance_id):
|
||||
self.conn = conn
|
||||
self.instance_id = instance_id
|
||||
self.last_updated = datetime.datetime.min
|
||||
self.cputime = 0
|
||||
self.cputime_last_updated = None
|
||||
|
||||
init_rrd(self, 'cpu')
|
||||
init_rrd(self, 'net')
|
||||
init_rrd(self, 'disk')
|
||||
|
||||
def needs_update(self):
|
||||
"""
|
||||
Indicates whether this instance is due to have its statistics updated.
|
||||
"""
|
||||
delta = utcnow() - self.last_updated
|
||||
return delta.seconds >= FLAGS.monitoring_instances_step
|
||||
|
||||
def update(self):
|
||||
"""
|
||||
Updates the instances statistics and stores the resulting graphs
|
||||
in the internal object store on the cloud controller.
|
||||
"""
|
||||
LOG.debug(_('updating %s...'), self.instance_id)
|
||||
|
||||
try:
|
||||
data = self.fetch_cpu_stats()
|
||||
if data is not None:
|
||||
LOG.debug('CPU: %s', data)
|
||||
update_rrd(self, 'cpu', data)
|
||||
|
||||
data = self.fetch_net_stats()
|
||||
LOG.debug('NET: %s', data)
|
||||
update_rrd(self, 'net', data)
|
||||
|
||||
data = self.fetch_disk_stats()
|
||||
LOG.debug('DISK: %s', data)
|
||||
update_rrd(self, 'disk', data)
|
||||
|
||||
# TODO(devcamcar): Turn these into pool.ProcessPool.execute() calls
|
||||
# and make the methods @defer.inlineCallbacks.
|
||||
graph_cpu(self, '1d')
|
||||
graph_cpu(self, '1w')
|
||||
graph_cpu(self, '1m')
|
||||
|
||||
graph_net(self, '1d')
|
||||
graph_net(self, '1w')
|
||||
graph_net(self, '1m')
|
||||
|
||||
graph_disk(self, '1d')
|
||||
graph_disk(self, '1w')
|
||||
graph_disk(self, '1m')
|
||||
except Exception:
|
||||
LOG.exception(_('unexpected error during update'))
|
||||
|
||||
self.last_updated = utcnow()
|
||||
|
||||
def get_rrd_path(self):
|
||||
"""
|
||||
Returns the path to where RRD files are stored.
|
||||
"""
|
||||
return os.path.join(FLAGS.monitoring_rrd_path, self.instance_id)
|
||||
|
||||
def fetch_cpu_stats(self):
|
||||
"""
|
||||
Returns cpu usage statistics for this instance.
|
||||
"""
|
||||
info = self.conn.get_info(self.instance_id)
|
||||
|
||||
# Get the previous values.
|
||||
cputime_last = self.cputime
|
||||
cputime_last_updated = self.cputime_last_updated
|
||||
|
||||
# Get the raw CPU time used in nanoseconds.
|
||||
self.cputime = float(info['cpu_time'])
|
||||
self.cputime_last_updated = utcnow()
|
||||
|
||||
LOG.debug('CPU: %d', self.cputime)
|
||||
|
||||
# Skip calculation on first pass. Need delta to get a meaningful value.
|
||||
if cputime_last_updated is None:
|
||||
return None
|
||||
|
||||
# Calculate the number of seconds between samples.
|
||||
d = self.cputime_last_updated - cputime_last_updated
|
||||
t = d.days * 86400 + d.seconds
|
||||
|
||||
LOG.debug('t = %d', t)
|
||||
|
||||
# Calculate change over time in number of nanoseconds of CPU time used.
|
||||
cputime_delta = self.cputime - cputime_last
|
||||
|
||||
LOG.debug('cputime_delta = %s', cputime_delta)
|
||||
|
||||
# Get the number of virtual cpus in this domain.
|
||||
vcpus = int(info['num_cpu'])
|
||||
|
||||
LOG.debug('vcpus = %d', vcpus)
|
||||
|
||||
# Calculate CPU % used and cap at 100.
|
||||
return min(cputime_delta / (t * vcpus * 1.0e9) * 100, 100)
|
||||
|
||||
def fetch_disk_stats(self):
|
||||
"""
|
||||
Returns disk usage statistics for this instance.
|
||||
"""
|
||||
rd = 0
|
||||
wr = 0
|
||||
|
||||
disks = self.conn.get_disks(self.instance_id)
|
||||
|
||||
# Aggregate the read and write totals.
|
||||
for disk in disks:
|
||||
try:
|
||||
rd_req, rd_bytes, wr_req, wr_bytes, errs = \
|
||||
self.conn.block_stats(self.instance_id, disk)
|
||||
rd += rd_bytes
|
||||
wr += wr_bytes
|
||||
except TypeError:
|
||||
iid = self.instance_id
|
||||
LOG.error(_('Cannot get blockstats for "%(disk)s"'
|
||||
' on "%(iid)s"') % locals())
|
||||
raise
|
||||
|
||||
return '%d:%d' % (rd, wr)
|
||||
|
||||
def fetch_net_stats(self):
|
||||
"""
|
||||
Returns network usage statistics for this instance.
|
||||
"""
|
||||
rx = 0
|
||||
tx = 0
|
||||
|
||||
interfaces = self.conn.get_interfaces(self.instance_id)
|
||||
|
||||
# Aggregate the in and out totals.
|
||||
for interface in interfaces:
|
||||
try:
|
||||
stats = self.conn.interface_stats(self.instance_id, interface)
|
||||
rx += stats[0]
|
||||
tx += stats[4]
|
||||
except TypeError:
|
||||
iid = self.instance_id
|
||||
LOG.error(_('Cannot get ifstats for "%(interface)s"'
|
||||
' on "%(iid)s"') % locals())
|
||||
raise
|
||||
|
||||
return '%d:%d' % (rx, tx)
|
||||
|
||||
|
||||
class InstanceMonitor(object, service.Service):
|
||||
"""
|
||||
Monitors the running instances of the current machine.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize the monitoring loop.
|
||||
"""
|
||||
self._instances = {}
|
||||
self._loop = task.LoopingCall(self.updateInstances)
|
||||
|
||||
def startService(self):
|
||||
self._instances = {}
|
||||
self._loop.start(interval=FLAGS.monitoring_instances_delay)
|
||||
service.Service.startService(self)
|
||||
|
||||
def stopService(self):
|
||||
self._loop.stop()
|
||||
service.Service.stopService(self)
|
||||
|
||||
def updateInstances(self):
|
||||
"""
|
||||
Update resource usage for all running instances.
|
||||
"""
|
||||
try:
|
||||
conn = virt_connection.get_connection(read_only=True)
|
||||
except Exception, exn:
|
||||
LOG.exception(_('unexpected exception getting connection'))
|
||||
time.sleep(FLAGS.monitoring_instances_delay)
|
||||
return
|
||||
|
||||
domain_ids = conn.list_instances()
|
||||
try:
|
||||
self.updateInstances_(conn, domain_ids)
|
||||
except Exception, exn:
|
||||
LOG.exception('updateInstances_')
|
||||
|
||||
def updateInstances_(self, conn, domain_ids):
|
||||
for domain_id in domain_ids:
|
||||
if not domain_id in self._instances:
|
||||
instance = Instance(conn, domain_id)
|
||||
self._instances[domain_id] = instance
|
||||
LOG.debug(_('Found instance: %s'), domain_id)
|
||||
|
||||
for key in self._instances.keys():
|
||||
instance = self._instances[key]
|
||||
if instance.needs_update():
|
||||
instance.update()
|
||||
+31
-64
@@ -18,9 +18,8 @@
|
||||
|
||||
"""RequestContext: context for requests that persist through all of nova."""
|
||||
|
||||
import random
|
||||
import uuid
|
||||
|
||||
from nova import exception
|
||||
from nova import utils
|
||||
|
||||
|
||||
@@ -31,86 +30,54 @@ class RequestContext(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, user, project, is_admin=None, read_deleted=False,
|
||||
remote_address=None, timestamp=None, request_id=None):
|
||||
if hasattr(user, 'id'):
|
||||
self._user = user
|
||||
self.user_id = user.id
|
||||
else:
|
||||
self._user = None
|
||||
self.user_id = user
|
||||
if hasattr(project, 'id'):
|
||||
self._project = project
|
||||
self.project_id = project.id
|
||||
else:
|
||||
self._project = None
|
||||
self.project_id = project
|
||||
if is_admin is None:
|
||||
if self.user_id and self.user:
|
||||
self.is_admin = self.user.is_admin()
|
||||
else:
|
||||
self.is_admin = False
|
||||
else:
|
||||
self.is_admin = is_admin
|
||||
def __init__(self, user_id, project_id, is_admin=None, read_deleted=False,
|
||||
roles=None, remote_address=None, timestamp=None,
|
||||
request_id=None, auth_token=None):
|
||||
self.user_id = user_id
|
||||
self.project_id = project_id
|
||||
self.roles = roles or []
|
||||
self.is_admin = is_admin
|
||||
if self.is_admin is None:
|
||||
self.admin = 'admin' in self.roles
|
||||
self.read_deleted = read_deleted
|
||||
self.remote_address = remote_address
|
||||
if not timestamp:
|
||||
timestamp = utils.utcnow()
|
||||
if isinstance(timestamp, str) or isinstance(timestamp, unicode):
|
||||
timestamp = utils.parse_isotime(timestamp)
|
||||
if isinstance(timestamp, basestring):
|
||||
timestamp = utils.parse_strtime(timestamp)
|
||||
self.timestamp = timestamp
|
||||
if not request_id:
|
||||
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-'
|
||||
request_id = ''.join([random.choice(chars) for x in xrange(20)])
|
||||
request_id = unicode(uuid.uuid4())
|
||||
self.request_id = request_id
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
# NOTE(vish): Delay import of manager, so that we can import this
|
||||
# file from manager.
|
||||
from nova.auth import manager
|
||||
if not self._user:
|
||||
try:
|
||||
self._user = manager.AuthManager().get_user(self.user_id)
|
||||
except exception.NotFound:
|
||||
pass
|
||||
return self._user
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
# NOTE(vish): Delay import of manager, so that we can import this
|
||||
# file from manager.
|
||||
from nova.auth import manager
|
||||
if not self._project:
|
||||
try:
|
||||
auth_manager = manager.AuthManager()
|
||||
self._project = auth_manager.get_project(self.project_id)
|
||||
except exception.NotFound:
|
||||
pass
|
||||
return self._project
|
||||
self.auth_token = auth_token
|
||||
|
||||
def to_dict(self):
|
||||
return {'user': self.user_id,
|
||||
'project': self.project_id,
|
||||
return {'user_id': self.user_id,
|
||||
'project_id': self.project_id,
|
||||
'is_admin': self.is_admin,
|
||||
'read_deleted': self.read_deleted,
|
||||
'roles': self.roles,
|
||||
'remote_address': self.remote_address,
|
||||
'timestamp': utils.isotime(self.timestamp),
|
||||
'request_id': self.request_id}
|
||||
'timestamp': utils.strtime(self.timestamp),
|
||||
'request_id': self.request_id,
|
||||
'auth_token': self.auth_token}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, values):
|
||||
return cls(**values)
|
||||
|
||||
def elevated(self, read_deleted=False):
|
||||
def elevated(self, read_deleted=None):
|
||||
"""Return a version of this context with admin flag set."""
|
||||
return RequestContext(self.user_id,
|
||||
self.project_id,
|
||||
True,
|
||||
read_deleted,
|
||||
self.remote_address,
|
||||
self.timestamp,
|
||||
self.request_id)
|
||||
rd = self.read_deleted if read_deleted is None else read_deleted
|
||||
return RequestContext(user_id=self.user_id,
|
||||
project_id=self.project_id,
|
||||
is_admin=True,
|
||||
read_deleted=rd,
|
||||
roles=self.roles,
|
||||
remote_address=self.remote_address,
|
||||
timestamp=self.timestamp,
|
||||
request_id=self.request_id,
|
||||
auth_token=self.auth_token)
|
||||
|
||||
|
||||
def get_admin_context(read_deleted=False):
|
||||
|
||||
@@ -62,7 +62,7 @@ def is_user_context(context):
|
||||
def authorize_project_context(context, project_id):
|
||||
"""Ensures a request has permission to access the given project."""
|
||||
if is_user_context(context):
|
||||
if not context.project:
|
||||
if not context.project_id:
|
||||
raise exception.NotAuthorized()
|
||||
elif context.project_id != project_id:
|
||||
raise exception.NotAuthorized()
|
||||
@@ -71,7 +71,7 @@ def authorize_project_context(context, project_id):
|
||||
def authorize_user_context(context, user_id):
|
||||
"""Ensures a request has permission to access the given user."""
|
||||
if is_user_context(context):
|
||||
if not context.user:
|
||||
if not context.user_id:
|
||||
raise exception.NotAuthorized()
|
||||
elif context.user_id != user_id:
|
||||
raise exception.NotAuthorized()
|
||||
@@ -1312,7 +1312,7 @@ def instance_get_fixed_addresses_v6(context, instance_id):
|
||||
# combine prefixes, macs, and project_id into (prefix,mac,p_id) tuples
|
||||
prefix_mac_tuples = zip(prefixes, macs, [project_id for m in macs])
|
||||
# return list containing ipv6 address for each tuple
|
||||
return [ipv6.to_global_ipv6(*t) for t in prefix_mac_tuples]
|
||||
return [ipv6.to_global(*t) for t in prefix_mac_tuples]
|
||||
|
||||
|
||||
@require_context
|
||||
@@ -1680,7 +1680,8 @@ def network_get_by_bridge(context, bridge):
|
||||
def network_get_by_cidr(context, cidr):
|
||||
session = get_session()
|
||||
result = session.query(models.Network).\
|
||||
filter_by(cidr=cidr).first()
|
||||
filter(or_(models.Network.cidr == cidr,
|
||||
models.Network.cidr_v6 == cidr)).first()
|
||||
|
||||
if not result:
|
||||
raise exception.NetworkNotFoundForCidr(cidr=cidr)
|
||||
@@ -3041,13 +3042,18 @@ def instance_type_get_by_name(context, name):
|
||||
@require_context
|
||||
def instance_type_get_by_flavor_id(context, id):
|
||||
"""Returns a dict describing specific flavor_id"""
|
||||
try:
|
||||
flavor_id = int(id)
|
||||
except ValueError:
|
||||
raise exception.FlavorNotFound(flavor_id=id)
|
||||
|
||||
session = get_session()
|
||||
inst_type = session.query(models.InstanceTypes).\
|
||||
options(joinedload('extra_specs')).\
|
||||
filter_by(flavorid=int(id)).\
|
||||
filter_by(flavorid=flavor_id).\
|
||||
first()
|
||||
if not inst_type:
|
||||
raise exception.FlavorNotFound(flavor_id=id)
|
||||
raise exception.FlavorNotFound(flavor_id=flavor_id)
|
||||
else:
|
||||
return _dict_with_extra_specs(inst_type)
|
||||
|
||||
@@ -3247,8 +3253,8 @@ def agent_build_destroy(context, agent_build_id):
|
||||
with session.begin():
|
||||
session.query(models.AgentBuild).\
|
||||
filter_by(id=agent_build_id).\
|
||||
update({'deleted': 1,
|
||||
'deleted_at': datetime.datetime.utcnow(),
|
||||
update({'deleted': True,
|
||||
'deleted_at': utils.utcnow(),
|
||||
'updated_at': literal_column('updated_at')})
|
||||
|
||||
|
||||
@@ -3297,7 +3303,7 @@ def instance_type_extra_specs_delete(context, instance_type_id, key):
|
||||
def instance_type_extra_specs_get_item(context, instance_type_id, key):
|
||||
session = get_session()
|
||||
|
||||
sppec_result = session.query(models.InstanceTypeExtraSpecs).\
|
||||
spec_result = session.query(models.InstanceTypeExtraSpecs).\
|
||||
filter_by(instance_type_id=instance_type_id).\
|
||||
filter_by(key=key).\
|
||||
filter_by(deleted=False).\
|
||||
|
||||
@@ -177,14 +177,6 @@ class Instance(BASE, NovaBase):
|
||||
user_id = Column(String(255))
|
||||
project_id = Column(String(255))
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
return auth.manager.AuthManager().get_user(self.user_id)
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
return auth.manager.AuthManager().get_project(self.project_id)
|
||||
|
||||
image_ref = Column(String(255))
|
||||
kernel_id = Column(String(255))
|
||||
ramdisk_id = Column(String(255))
|
||||
@@ -465,14 +457,6 @@ class SecurityGroup(BASE, NovaBase):
|
||||
'Instance.deleted == False)',
|
||||
backref='security_groups')
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
return auth.manager.AuthManager().get_user(self.user_id)
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
return auth.manager.AuthManager().get_project(self.project_id)
|
||||
|
||||
|
||||
class SecurityGroupIngressRule(BASE, NovaBase):
|
||||
"""Represents a rule in a security group."""
|
||||
|
||||
@@ -692,3 +692,11 @@ class PasteConfigNotFound(NotFound):
|
||||
|
||||
class PasteAppNotFound(NotFound):
|
||||
message = _("Could not load paste app '%(name)s' from %(path)s")
|
||||
|
||||
|
||||
class CannotResizeToSameSize(NovaException):
|
||||
message = _("When resizing, instances must change size!")
|
||||
|
||||
|
||||
class CannotResizeToSmallerSize(NovaException):
|
||||
message = _("Resizing to a smaller size is not supported.")
|
||||
|
||||
+1
-1
@@ -343,7 +343,7 @@ DEFINE_string('lock_path', os.path.join(os.path.dirname(__file__), '../'),
|
||||
'Directory for lock files')
|
||||
DEFINE_string('logdir', None, 'output to a per-service log file in named '
|
||||
'directory')
|
||||
|
||||
DEFINE_integer('logfile_mode', 0644, 'Default file mode of the logs.')
|
||||
DEFINE_string('sqlite_db', 'nova.sqlite', 'file name for sqlite')
|
||||
DEFINE_string('sql_connection',
|
||||
'sqlite:///$state_path/$sqlite_db',
|
||||
|
||||
@@ -83,11 +83,16 @@ class GlanceImageService(service.BaseImageService):
|
||||
|
||||
client = property(_get_client, _set_client)
|
||||
|
||||
def _set_client_context(self, context):
|
||||
"""Sets the client's auth token."""
|
||||
self.client.set_auth_token(context.auth_token)
|
||||
|
||||
def index(self, context, filters=None, marker=None, limit=None):
|
||||
"""Calls out to Glance for a list of images available."""
|
||||
# NOTE(sirp): We need to use `get_images_detailed` and not
|
||||
# `get_images` here because we need `is_public` and `properties`
|
||||
# included so we can filter by user
|
||||
self._set_client_context(context)
|
||||
filtered = []
|
||||
filters = filters or {}
|
||||
if 'is_public' not in filters:
|
||||
@@ -104,6 +109,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
|
||||
def detail(self, context, filters=None, marker=None, limit=None):
|
||||
"""Calls out to Glance for a list of detailed image information."""
|
||||
self._set_client_context(context)
|
||||
filtered = []
|
||||
filters = filters or {}
|
||||
if 'is_public' not in filters:
|
||||
@@ -120,6 +126,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
|
||||
def show(self, context, image_id):
|
||||
"""Returns a dict with image data for the given opaque image id."""
|
||||
self._set_client_context(context)
|
||||
try:
|
||||
image_meta = self.client.get_image_meta(image_id)
|
||||
except glance_exception.NotFound:
|
||||
@@ -143,6 +150,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
|
||||
def get(self, context, image_id, data):
|
||||
"""Calls out to Glance for metadata and data and writes data."""
|
||||
self._set_client_context(context)
|
||||
try:
|
||||
image_meta, image_chunks = self.client.get_image(image_id)
|
||||
except glance_exception.NotFound:
|
||||
@@ -160,6 +168,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
:raises: AlreadyExists if the image already exist.
|
||||
|
||||
"""
|
||||
self._set_client_context(context)
|
||||
# Translate Base -> Service
|
||||
LOG.debug(_('Creating image in Glance. Metadata passed in %s'),
|
||||
image_meta)
|
||||
@@ -182,6 +191,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
:raises: ImageNotFound if the image does not exist.
|
||||
|
||||
"""
|
||||
self._set_client_context(context)
|
||||
# NOTE(vish): show is to check if image is available
|
||||
self.show(context, image_id)
|
||||
try:
|
||||
@@ -198,6 +208,7 @@ class GlanceImageService(service.BaseImageService):
|
||||
:raises: ImageNotFound if the image does not exist.
|
||||
|
||||
"""
|
||||
self._set_client_context(context)
|
||||
# NOTE(vish): show is to check if image is available
|
||||
self.show(context, image_id)
|
||||
try:
|
||||
|
||||
+8
-6
@@ -34,7 +34,6 @@ from nova import flags
|
||||
from nova import image
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.image import service
|
||||
from nova.api.ec2 import ec2utils
|
||||
|
||||
@@ -43,6 +42,10 @@ LOG = logging.getLogger("nova.image.s3")
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('image_decryption_dir', '/tmp',
|
||||
'parent dir for tempdir used for image decryption')
|
||||
flags.DEFINE_string('s3_access_key', 'notchecked',
|
||||
'access key to use for s3 server for images')
|
||||
flags.DEFINE_string('s3_secret_key', 'notchecked',
|
||||
'secret key to use for s3 server for images')
|
||||
|
||||
|
||||
class S3ImageService(service.BaseImageService):
|
||||
@@ -82,11 +85,10 @@ class S3ImageService(service.BaseImageService):
|
||||
|
||||
@staticmethod
|
||||
def _conn(context):
|
||||
# TODO(vish): is there a better way to get creds to sign
|
||||
# for the user?
|
||||
access = manager.AuthManager().get_access_key(context.user,
|
||||
context.project)
|
||||
secret = str(context.user.secret)
|
||||
# NOTE(vish): access and secret keys for s3 server are not
|
||||
# checked in nova-objectstore
|
||||
access = FLAGS.s3_access_key
|
||||
secret = FLAGS.s3_secret_key
|
||||
calling = boto.s3.connection.OrdinaryCallingFormat()
|
||||
return boto.s3.connection.S3Connection(aws_access_key_id=access,
|
||||
aws_secret_access_key=secret,
|
||||
|
||||
+3
-2
@@ -43,8 +43,8 @@ from nova import version
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('logging_context_format_string',
|
||||
'%(asctime)s %(levelname)s %(name)s '
|
||||
'[%(request_id)s %(user)s '
|
||||
'%(project)s] %(message)s',
|
||||
'[%(request_id)s %(user_id)s '
|
||||
'%(project_id)s] %(message)s',
|
||||
'format string to use for log messages with context')
|
||||
flags.DEFINE_string('logging_default_format_string',
|
||||
'%(asctime)s %(levelname)s %(name)s [-] '
|
||||
@@ -257,6 +257,7 @@ class NovaRootLogger(NovaLogger):
|
||||
self.filelog = WatchedFileHandler(logpath)
|
||||
self.addHandler(self.filelog)
|
||||
self.logpath = logpath
|
||||
os.chmod(self.logpath, FLAGS.logfile_mode)
|
||||
else:
|
||||
self.removeHandler(self.filelog)
|
||||
self.addHandler(self.streamlog)
|
||||
|
||||
+37
-27
@@ -410,8 +410,11 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
kwargs can contain fixed_ips to circumvent another db lookup
|
||||
"""
|
||||
instance_id = kwargs.pop('instance_id')
|
||||
fixed_ips = kwargs.get('fixed_ips') or \
|
||||
try:
|
||||
fixed_ips = kwargs.get('fixed_ips') or \
|
||||
self.db.fixed_ip_get_by_instance(context, instance_id)
|
||||
except exceptions.FixedIpNotFoundForInstance:
|
||||
fixed_ips = []
|
||||
LOG.debug(_("network deallocation for instance |%s|"), instance_id,
|
||||
context=context)
|
||||
# deallocate fixed ips
|
||||
@@ -551,15 +554,17 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
# with a network, or a cluster of computes with a network
|
||||
# and use that network here with a method like
|
||||
# network_get_by_compute_host
|
||||
address = self.db.fixed_ip_associate_pool(context.elevated(),
|
||||
network['id'],
|
||||
instance_id)
|
||||
vif = self.db.virtual_interface_get_by_instance_and_network(context,
|
||||
instance_id,
|
||||
network['id'])
|
||||
values = {'allocated': True,
|
||||
'virtual_interface_id': vif['id']}
|
||||
self.db.fixed_ip_update(context, address, values)
|
||||
address = None
|
||||
if network['cidr']:
|
||||
address = self.db.fixed_ip_associate_pool(context.elevated(),
|
||||
network['id'],
|
||||
instance_id)
|
||||
get_vif = self.db.virtual_interface_get_by_instance_and_network
|
||||
vif = get_vif(context, instance_id, network['id'])
|
||||
values = {'allocated': True,
|
||||
'virtual_interface_id': vif['id']}
|
||||
self.db.fixed_ip_update(context, address, values)
|
||||
|
||||
self._setup_network(context, network)
|
||||
return address
|
||||
|
||||
@@ -613,34 +618,39 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
network_size, cidr_v6, gateway_v6, bridge,
|
||||
bridge_interface, dns1=None, dns2=None, **kwargs):
|
||||
"""Create networks based on parameters."""
|
||||
fixed_net = netaddr.IPNetwork(cidr)
|
||||
if FLAGS.use_ipv6:
|
||||
if cidr_v6:
|
||||
fixed_net_v6 = netaddr.IPNetwork(cidr_v6)
|
||||
significant_bits_v6 = 64
|
||||
network_size_v6 = 1 << 64
|
||||
|
||||
for index in range(num_networks):
|
||||
start = index * network_size
|
||||
if cidr:
|
||||
fixed_net = netaddr.IPNetwork(cidr)
|
||||
significant_bits = 32 - int(math.log(network_size, 2))
|
||||
cidr = '%s/%s' % (fixed_net[start], significant_bits)
|
||||
project_net = netaddr.IPNetwork(cidr)
|
||||
|
||||
for index in range(num_networks):
|
||||
net = {}
|
||||
net['bridge'] = bridge
|
||||
net['bridge_interface'] = bridge_interface
|
||||
net['dns1'] = dns1
|
||||
net['dns2'] = dns2
|
||||
net['cidr'] = cidr
|
||||
net['multi_host'] = multi_host
|
||||
net['netmask'] = str(project_net.netmask)
|
||||
net['gateway'] = str(project_net[1])
|
||||
net['broadcast'] = str(project_net.broadcast)
|
||||
net['dhcp_start'] = str(project_net[2])
|
||||
|
||||
if cidr:
|
||||
start = index * network_size
|
||||
project_net = netaddr.IPNetwork('%s/%s' % (fixed_net[start],
|
||||
significant_bits))
|
||||
net['cidr'] = str(project_net)
|
||||
net['multi_host'] = multi_host
|
||||
net['netmask'] = str(project_net.netmask)
|
||||
net['gateway'] = str(project_net[1])
|
||||
net['broadcast'] = str(project_net.broadcast)
|
||||
net['dhcp_start'] = str(project_net[2])
|
||||
|
||||
if num_networks > 1:
|
||||
net['label'] = '%s_%d' % (label, index)
|
||||
else:
|
||||
net['label'] = label
|
||||
|
||||
if FLAGS.use_ipv6:
|
||||
if cidr_v6:
|
||||
start_v6 = index * network_size_v6
|
||||
cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6],
|
||||
significant_bits_v6)
|
||||
@@ -673,11 +683,11 @@ class NetworkManager(manager.SchedulerDependentManager):
|
||||
# None if network with cidr or cidr_v6 already exists
|
||||
network = self.db.network_create_safe(context, net)
|
||||
|
||||
if network:
|
||||
if not network:
|
||||
raise ValueError(_('Network already exists!'))
|
||||
|
||||
if network and cidr:
|
||||
self._create_fixed_ips(context, network['id'])
|
||||
else:
|
||||
raise ValueError(_('Network with cidr %s already exists') %
|
||||
cidr)
|
||||
|
||||
@property
|
||||
def _bottom_reserved_ips(self): # pylint: disable=R0201
|
||||
|
||||
@@ -80,6 +80,10 @@ def notify(publisher_id, event_type, priority, payload):
|
||||
if priority not in log_levels:
|
||||
raise BadPriorityException(
|
||||
_('%s not in valid priorities' % priority))
|
||||
|
||||
# Ensure everything is JSON serializable.
|
||||
payload = utils.to_primitive(payload, convert_instances=True)
|
||||
|
||||
driver = utils.import_object(FLAGS.notification_driver)
|
||||
msg = dict(message_id=str(uuid.uuid4()),
|
||||
publisher_id=publisher_id,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from nova.utils import import_object
|
||||
from nova.rpc.common import RemoteError, LOG
|
||||
from nova import flags
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('rpc_backend',
|
||||
'nova.rpc.amqp',
|
||||
"The messaging module to use, defaults to AMQP.")
|
||||
|
||||
RPCIMPL = import_object(FLAGS.rpc_backend)
|
||||
|
||||
|
||||
def create_connection(new=True):
|
||||
return RPCIMPL.Connection.instance(new=True)
|
||||
|
||||
|
||||
def create_consumer(conn, topic, proxy, fanout=False):
|
||||
if fanout:
|
||||
return RPCIMPL.FanoutAdapterConsumer(
|
||||
connection=conn,
|
||||
topic=topic,
|
||||
proxy=proxy)
|
||||
else:
|
||||
return RPCIMPL.TopicAdapterConsumer(
|
||||
connection=conn,
|
||||
topic=topic,
|
||||
proxy=proxy)
|
||||
|
||||
|
||||
def create_consumer_set(conn, consumers):
|
||||
return RPCIMPL.ConsumerSet(connection=conn, consumer_list=consumers)
|
||||
|
||||
|
||||
def call(context, topic, msg):
|
||||
return RPCIMPL.call(context, topic, msg)
|
||||
|
||||
|
||||
def cast(context, topic, msg):
|
||||
return RPCIMPL.cast(context, topic, msg)
|
||||
|
||||
|
||||
def fanout_cast(context, topic, msg):
|
||||
return RPCIMPL.fanout_cast(context, topic, msg)
|
||||
|
||||
|
||||
def multicall(context, topic, msg):
|
||||
return RPCIMPL.multicall(context, topic, msg)
|
||||
@@ -44,9 +44,7 @@ from nova import fakerabbit
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.rpc')
|
||||
from nova.rpc.common import RemoteError, LOG
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -418,25 +416,6 @@ def msg_reply(msg_id, reply=None, failure=None):
|
||||
publisher.close()
|
||||
|
||||
|
||||
class RemoteError(exception.Error):
|
||||
"""Signifies that a remote class has raised an exception.
|
||||
|
||||
Containes a string representation of the type of the original exception,
|
||||
the value of the original exception, and the traceback. These are
|
||||
sent to the parent as a joined string so printing the exception
|
||||
contains all of the relevent info.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, exc_type, value, traceback):
|
||||
self.exc_type = exc_type
|
||||
self.value = value
|
||||
self.traceback = traceback
|
||||
super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
|
||||
value,
|
||||
traceback))
|
||||
|
||||
|
||||
def _unpack_context(msg):
|
||||
"""Unpack context from msg."""
|
||||
context_dict = {}
|
||||
@@ -0,0 +1,23 @@
|
||||
from nova import exception
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger('nova.rpc')
|
||||
|
||||
|
||||
class RemoteError(exception.Error):
|
||||
"""Signifies that a remote class has raised an exception.
|
||||
|
||||
Containes a string representation of the type of the original exception,
|
||||
the value of the original exception, and the traceback. These are
|
||||
sent to the parent as a joined string so printing the exception
|
||||
contains all of the relevent info.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, exc_type, value, traceback):
|
||||
self.exc_type = exc_type
|
||||
self.value = value
|
||||
self.traceback = traceback
|
||||
super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
|
||||
value,
|
||||
traceback))
|
||||
@@ -28,6 +28,7 @@ from nova import flags
|
||||
from nova import log as logging
|
||||
from nova.scheduler import zone_aware_scheduler
|
||||
from nova import utils
|
||||
from nova import exception
|
||||
|
||||
LOG = logging.getLogger('nova.scheduler.least_cost')
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ class ZoneAwareScheduler(driver.Scheduler):
|
||||
decryptor = crypto.decryptor(FLAGS.build_plan_encryption_key)
|
||||
try:
|
||||
json_entry = decryptor(blob)
|
||||
return json.dumps(entry)
|
||||
return json.dumps(json_entry)
|
||||
except M2Crypto.EVP.EVPError:
|
||||
pass
|
||||
return None
|
||||
|
||||
+12
-16
@@ -149,26 +149,22 @@ class Service(object):
|
||||
if 'nova-compute' == self.binary:
|
||||
self.manager.update_available_resource(ctxt)
|
||||
|
||||
self.conn = rpc.Connection.instance(new=True)
|
||||
self.conn = rpc.create_connection(new=True)
|
||||
logging.debug("Creating Consumer connection for Service %s" %
|
||||
self.topic)
|
||||
|
||||
# Share this same connection for these Consumers
|
||||
consumer_all = rpc.TopicAdapterConsumer(
|
||||
connection=self.conn,
|
||||
topic=self.topic,
|
||||
proxy=self)
|
||||
consumer_node = rpc.TopicAdapterConsumer(
|
||||
connection=self.conn,
|
||||
topic='%s.%s' % (self.topic, self.host),
|
||||
proxy=self)
|
||||
fanout = rpc.FanoutAdapterConsumer(
|
||||
connection=self.conn,
|
||||
topic=self.topic,
|
||||
proxy=self)
|
||||
consumer_set = rpc.ConsumerSet(
|
||||
connection=self.conn,
|
||||
consumer_list=[consumer_all, consumer_node, fanout])
|
||||
consumer_all = rpc.create_consumer(self.conn, self.topic, self,
|
||||
fanout=False)
|
||||
|
||||
node_topic = '%s.%s' % (self.topic, self.host)
|
||||
consumer_node = rpc.create_consumer(self.conn, node_topic, self,
|
||||
fanout=False)
|
||||
|
||||
fanout = rpc.create_consumer(self.conn, self.topic, self, fanout=True)
|
||||
|
||||
consumers = [consumer_all, consumer_node, fanout]
|
||||
consumer_set = rpc.create_consumer_set(self.conn, consumers)
|
||||
|
||||
# Wait forever, processing these consumers
|
||||
def _wait():
|
||||
|
||||
@@ -99,9 +99,7 @@ class TestCase(unittest.TestCase):
|
||||
self.flag_overrides = {}
|
||||
self.injected = []
|
||||
self._services = []
|
||||
self._monkey_patch_attach()
|
||||
self._original_flags = FLAGS.FlagValuesDict()
|
||||
rpc.ConnectionPool = rpc.Pool(max_size=FLAGS.rpc_conn_pool_size)
|
||||
|
||||
def tearDown(self):
|
||||
"""Runs after each test method to tear down test environment."""
|
||||
@@ -126,9 +124,6 @@ class TestCase(unittest.TestCase):
|
||||
# Reset any overriden flags
|
||||
self.reset_flags()
|
||||
|
||||
# Reset our monkey-patches
|
||||
rpc.Consumer.attach_to_eventlet = self.original_attach
|
||||
|
||||
# Stop any timers
|
||||
for x in self.injected:
|
||||
try:
|
||||
@@ -172,17 +167,6 @@ class TestCase(unittest.TestCase):
|
||||
self._services.append(svc)
|
||||
return svc
|
||||
|
||||
def _monkey_patch_attach(self):
|
||||
self.original_attach = rpc.Consumer.attach_to_eventlet
|
||||
|
||||
def _wrapped(inner_self):
|
||||
rv = self.original_attach(inner_self)
|
||||
self.injected.append(rv)
|
||||
return rv
|
||||
|
||||
_wrapped.func_name = self.original_attach.func_name
|
||||
rpc.Consumer.attach_to_eventlet = _wrapped
|
||||
|
||||
# Useful assertions
|
||||
def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
|
||||
"""Assert two dicts are equivalent.
|
||||
|
||||
@@ -74,12 +74,8 @@ class FloatingIpTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(FloatingIpTest, self).setUp()
|
||||
self.controller = FloatingIPController()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.stubs.Set(network.api.API, "get_floating_ip",
|
||||
network_api_get_floating_ip)
|
||||
self.stubs.Set(network.api.API, "list_floating_ips",
|
||||
@@ -96,7 +92,6 @@ class FloatingIpTest(test.TestCase):
|
||||
self._create_floating_ip()
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
self._delete_floating_ip()
|
||||
super(FloatingIpTest, self).tearDown()
|
||||
|
||||
@@ -111,6 +106,11 @@ class FloatingIpTest(test.TestCase):
|
||||
self.assertEqual(view['floating_ip']['fixed_ip'], None)
|
||||
self.assertEqual(view['floating_ip']['instance_id'], None)
|
||||
|
||||
def test_translate_floating_ip_view_dict(self):
|
||||
floating_ip = {'id': 0, 'address': '10.0.0.10', 'fixed_ip': None}
|
||||
view = _translate_floating_ip_view(floating_ip)
|
||||
self.assertTrue('floating_ip' in view)
|
||||
|
||||
def test_floating_ips_list(self):
|
||||
req = webob.Request.blank('/v1.1/os-floating-ips')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
@@ -42,22 +42,14 @@ def compute_api_remove_fixed_ip(self, context, instance_id, address):
|
||||
class FixedIpTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(FixedIpTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.stubs.Set(compute.api.API, "add_fixed_ip",
|
||||
compute_api_add_fixed_ip)
|
||||
self.stubs.Set(compute.api.API, "remove_fixed_ip",
|
||||
compute_api_remove_fixed_ip)
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(FixedIpTest, self).tearDown()
|
||||
|
||||
def test_add_fixed_ip(self):
|
||||
global last_add_fixed_ip
|
||||
last_add_fixed_ip = (None, None)
|
||||
|
||||
@@ -29,6 +29,7 @@ from glance.common import exception as glance_exc
|
||||
from nova import context
|
||||
from nova import exception as exc
|
||||
from nova import utils
|
||||
from nova import wsgi
|
||||
import nova.api.openstack.auth
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import auth
|
||||
@@ -40,14 +41,13 @@ import nova.image.fake
|
||||
from nova.image import glance
|
||||
from nova.image import service
|
||||
from nova.tests import fake_flags
|
||||
from nova.wsgi import Router
|
||||
|
||||
|
||||
class Context(object):
|
||||
pass
|
||||
|
||||
|
||||
class FakeRouter(Router):
|
||||
class FakeRouter(wsgi.Router):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@@ -68,21 +68,30 @@ def fake_auth_init(self, application):
|
||||
|
||||
@webob.dec.wsgify
|
||||
def fake_wsgi(self, req):
|
||||
req.environ['nova.context'] = context.RequestContext(1, 1)
|
||||
return self.application
|
||||
|
||||
|
||||
def wsgi_app(inner_app10=None, inner_app11=None):
|
||||
def wsgi_app(inner_app10=None, inner_app11=None, fake_auth=True):
|
||||
if not inner_app10:
|
||||
inner_app10 = openstack.APIRouterV10()
|
||||
if not inner_app11:
|
||||
inner_app11 = openstack.APIRouterV11()
|
||||
mapper = urlmap.URLMap()
|
||||
api10 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
|
||||
if fake_auth:
|
||||
ctxt = context.RequestContext('fake', 'fake')
|
||||
api10 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
|
||||
limits.RateLimitingMiddleware(inner_app10)))
|
||||
api11 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
api11 = openstack.FaultWrapper(wsgi.InjectContext(ctxt,
|
||||
limits.RateLimitingMiddleware(
|
||||
extensions.ExtensionMiddleware(inner_app11))))
|
||||
else:
|
||||
api10 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
limits.RateLimitingMiddleware(inner_app10)))
|
||||
api11 = openstack.FaultWrapper(auth.AuthMiddleware(
|
||||
limits.RateLimitingMiddleware(
|
||||
extensions.ExtensionMiddleware(inner_app11))))
|
||||
Auth = auth
|
||||
mapper = urlmap.URLMap()
|
||||
mapper['/v1.0'] = api10
|
||||
mapper['/v1.1'] = api11
|
||||
mapper['/'] = openstack.FaultWrapper(versions.Versions())
|
||||
@@ -104,8 +113,7 @@ def stub_out_key_pair_funcs(stubs, have_key_pair=True):
|
||||
|
||||
def stub_out_image_service(stubs):
|
||||
def fake_get_image_service(image_href):
|
||||
image_id = int(str(image_href).split('/')[-1])
|
||||
return (nova.image.fake.FakeImageService(), image_id)
|
||||
return (nova.image.fake.FakeImageService(), image_href)
|
||||
stubs.Set(nova.image, 'get_image_service', fake_get_image_service)
|
||||
stubs.Set(nova.image, 'get_default_image_service',
|
||||
lambda: nova.image.fake.FakeImageService())
|
||||
@@ -359,17 +367,18 @@ class FakeAuthManager(object):
|
||||
if admin is not None:
|
||||
user.admin = admin
|
||||
|
||||
def is_admin(self, user):
|
||||
def is_admin(self, user_id):
|
||||
user = self.get_user(user_id)
|
||||
return user.admin
|
||||
|
||||
def is_project_member(self, user, project):
|
||||
def is_project_member(self, user_id, project):
|
||||
if not isinstance(project, Project):
|
||||
try:
|
||||
project = self.get_project(project)
|
||||
except exc.NotFound:
|
||||
raise webob.exc.HTTPUnauthorized()
|
||||
return ((user.id in project.member_ids) or
|
||||
(user.id == project.project_manager_id))
|
||||
return ((user_id in project.member_ids) or
|
||||
(user_id == project.project_manager_id))
|
||||
|
||||
def create_project(self, name, manager_user, description=None,
|
||||
member_users=None):
|
||||
@@ -396,13 +405,13 @@ class FakeAuthManager(object):
|
||||
else:
|
||||
raise exc.NotFound
|
||||
|
||||
def get_projects(self, user=None):
|
||||
if not user:
|
||||
def get_projects(self, user_id=None):
|
||||
if not user_id:
|
||||
return FakeAuthManager.projects.values()
|
||||
else:
|
||||
return [p for p in FakeAuthManager.projects.values()
|
||||
if (user.id in p.member_ids) or
|
||||
(user.id == p.project_manager_id)]
|
||||
if (user_id in p.member_ids) or
|
||||
(user_id == p.project_manager_id)]
|
||||
|
||||
|
||||
class FakeRateLimiter(object):
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
import json
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from nova import flags
|
||||
@@ -41,7 +40,7 @@ def fake_admin_check(self, req):
|
||||
class AccountsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(AccountsTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.flags(allow_admin_api=True)
|
||||
self.stubs.Set(accounts.Controller, '__init__',
|
||||
fake_init)
|
||||
self.stubs.Set(accounts.Controller, '_check_admin',
|
||||
@@ -52,8 +51,6 @@ class AccountsTest(test.TestCase):
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
|
||||
self.allow_admin = FLAGS.allow_admin_api
|
||||
FLAGS.allow_admin_api = True
|
||||
fakemgr = fakes.FakeAuthManager()
|
||||
joeuser = User('id1', 'guy1', 'acc1', 'secret1', False)
|
||||
superuser = User('id2', 'guy2', 'acc2', 'secret2', True)
|
||||
@@ -62,11 +59,6 @@ class AccountsTest(test.TestCase):
|
||||
fakemgr.create_project('test1', joeuser)
|
||||
fakemgr.create_project('test2', superuser)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.allow_admin_api = self.allow_admin
|
||||
super(AccountsTest, self).tearDown()
|
||||
|
||||
def test_get_account(self):
|
||||
req = webob.Request.blank('/v1.0/accounts/test1')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
from paste import urlmap
|
||||
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import auth
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -33,21 +29,12 @@ class AdminAPITest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(AdminAPITest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.allow_admin = FLAGS.allow_admin_api
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.allow_admin_api = self.allow_admin
|
||||
super(AdminAPITest, self).tearDown()
|
||||
|
||||
def test_admin_enabled(self):
|
||||
FLAGS.allow_admin_api = True
|
||||
self.flags(allow_admin_api=True)
|
||||
# We should still be able to access public operations.
|
||||
req = webob.Request.blank('/v1.0/flavors')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
@@ -55,7 +42,7 @@ class AdminAPITest(test.TestCase):
|
||||
# TODO: Confirm admin operations are available.
|
||||
|
||||
def test_admin_disabled(self):
|
||||
FLAGS.allow_admin_api = False
|
||||
self.flags(allow_admin_api=False)
|
||||
# We should still be able to access public operations.
|
||||
req = webob.Request.blank('/v1.0/flavors')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
|
||||
import datetime
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
import webob.dec
|
||||
|
||||
import nova.api
|
||||
import nova.api.openstack.auth
|
||||
import nova.auth.manager
|
||||
from nova import auth
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import test
|
||||
@@ -35,7 +33,6 @@ class Test(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(Test, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
|
||||
'__init__', fakes.fake_auth_init)
|
||||
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
|
||||
@@ -45,7 +42,6 @@ class Test(test.TestCase):
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
fakes.fake_data_store = {}
|
||||
super(Test, self).tearDown()
|
||||
|
||||
@@ -57,7 +53,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '204 No Content')
|
||||
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
|
||||
self.assertEqual(result.headers['X-CDN-Management-Url'],
|
||||
@@ -73,7 +69,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '204 No Content')
|
||||
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
|
||||
self.assertEqual(result.headers['X-Server-Management-Url'],
|
||||
@@ -86,7 +82,7 @@ class Test(test.TestCase):
|
||||
self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter)
|
||||
req = webob.Request.blank('/v1.0/fake')
|
||||
req.headers['X-Auth-Token'] = token
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '200 OK')
|
||||
self.assertEqual(result.headers['X-Test-Success'], 'True')
|
||||
|
||||
@@ -110,7 +106,7 @@ class Test(test.TestCase):
|
||||
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-Token'] = 'token_hash'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
self.assertEqual(self.destroy_called, True)
|
||||
|
||||
@@ -124,7 +120,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '204 No Content')
|
||||
|
||||
token = result.headers['X-Auth-Token']
|
||||
@@ -132,7 +128,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/fake')
|
||||
req.headers['X-Auth-Token'] = token
|
||||
req.headers['X-Auth-Project-Id'] = 'user2_project'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '200 OK')
|
||||
self.assertEqual(result.headers['X-Test-Success'], 'True')
|
||||
|
||||
@@ -140,7 +136,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-User'] = 'unknown_user'
|
||||
req.headers['X-Auth-Key'] = 'unknown_user_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_bad_user_good_key(self):
|
||||
@@ -151,18 +147,18 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-User'] = 'unknown_user'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_no_user(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_bad_token(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-Token'] = 'unknown_token'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_bad_project(self):
|
||||
@@ -177,7 +173,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '204 No Content')
|
||||
|
||||
token = result.headers['X-Auth-Token']
|
||||
@@ -185,7 +181,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/fake')
|
||||
req.headers['X-Auth-Token'] = token
|
||||
req.headers['X-Auth-Project-Id'] = 'user2_project'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_not_existing_project(self):
|
||||
@@ -197,7 +193,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '204 No Content')
|
||||
|
||||
token = result.headers['X-Auth-Token']
|
||||
@@ -205,7 +201,7 @@ class Test(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/fake')
|
||||
req.headers['X-Auth-Token'] = token
|
||||
req.headers['X-Auth-Project-Id'] = 'unknown_project'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
|
||||
@@ -226,20 +222,19 @@ class TestFunctional(test.TestCase):
|
||||
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-Token'] = 'test_token_hash'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
def test_token_doesnotexist(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-Token'] = 'nonexistant_token_hash'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '401 Unauthorized')
|
||||
|
||||
|
||||
class TestLimiter(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestLimiter, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
|
||||
'__init__', fakes.fake_auth_init)
|
||||
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
|
||||
@@ -248,7 +243,6 @@ class TestLimiter(test.TestCase):
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
fakes.fake_data_store = {}
|
||||
super(TestLimiter, self).tearDown()
|
||||
|
||||
@@ -261,7 +255,7 @@ class TestLimiter(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.headers['X-Auth-User'] = 'user1'
|
||||
req.headers['X-Auth-Key'] = 'user1_key'
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
|
||||
|
||||
token = result.headers['X-Auth-Token']
|
||||
@@ -269,6 +263,6 @@ class TestLimiter(test.TestCase):
|
||||
req = webob.Request.blank('/v1.0/fake')
|
||||
req.method = 'POST'
|
||||
req.headers['X-Auth-Token'] = token
|
||||
result = req.get_response(fakes.wsgi_app())
|
||||
result = req.get_response(fakes.wsgi_app(fake_auth=False))
|
||||
self.assertEqual(result.status, '200 OK')
|
||||
self.assertEqual(result.headers['X-Test-Success'], 'True')
|
||||
|
||||
@@ -20,6 +20,7 @@ Test suites for 'common' code used throughout the OpenStack HTTP API.
|
||||
"""
|
||||
|
||||
import webob.exc
|
||||
import xml.dom.minidom as minidom
|
||||
|
||||
from webob import Request
|
||||
|
||||
@@ -265,3 +266,203 @@ class MiscFunctionsTest(test.TestCase):
|
||||
expected = '1.0'
|
||||
actual = common.get_version_from_href(fixture)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
||||
class MetadataXMLDeserializationTest(test.TestCase):
|
||||
|
||||
deserializer = common.MetadataXMLDeserializer()
|
||||
|
||||
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 MetadataXMLSerializationTest(test.TestCase):
|
||||
|
||||
def test_index(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
'one': 'two',
|
||||
'three': 'four',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="three">
|
||||
four
|
||||
</meta>
|
||||
<meta key="one">
|
||||
two
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_null(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
None: None,
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="None">
|
||||
None
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_unicode(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
u'three': u'Jos\xe9',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString(u"""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="three">
|
||||
Jos\xe9
|
||||
</meta>
|
||||
</metadata>
|
||||
""".encode("UTF-8").replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_show(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'meta': {
|
||||
'one': 'two',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'show')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
|
||||
two
|
||||
</meta>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_update_all(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
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 = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'meta': {
|
||||
'one': 'two',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'update')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
|
||||
two
|
||||
</meta>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_create(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
'key9': 'value9',
|
||||
'key2': 'value2',
|
||||
'key1': 'value1',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'create')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="key2">
|
||||
value2
|
||||
</meta>
|
||||
<meta key="key9">
|
||||
value9
|
||||
</meta>
|
||||
<meta key="key1">
|
||||
value1
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_delete(self):
|
||||
serializer = common.MetadataXMLSerializer()
|
||||
output = serializer.serialize(None, 'delete')
|
||||
self.assertEqual(output, '')
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
|
||||
import json
|
||||
import os.path
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import flavors
|
||||
@@ -80,11 +79,12 @@ class StubExtensionManager(object):
|
||||
return request_extensions
|
||||
|
||||
|
||||
class ExtensionControllerTest(unittest.TestCase):
|
||||
class ExtensionControllerTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
FLAGS.osapi_extensions_path = os.path.join(
|
||||
os.path.dirname(__file__), "extensions")
|
||||
super(ExtensionControllerTest, self).setUp()
|
||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||
self.flags(osapi_extensions_path=ext_path)
|
||||
|
||||
def test_list_extensions_json(self):
|
||||
app = openstack.APIRouterV11()
|
||||
@@ -109,8 +109,8 @@ class ExtensionControllerTest(unittest.TestCase):
|
||||
'updated': '2011-01-22T13:25:27-06:00',
|
||||
'description': 'The Fox In Socks Extension',
|
||||
'alias': 'FOXNSOX',
|
||||
'links': []
|
||||
}
|
||||
'links': [],
|
||||
},
|
||||
)
|
||||
|
||||
def test_get_extension_json(self):
|
||||
@@ -127,8 +127,8 @@ class ExtensionControllerTest(unittest.TestCase):
|
||||
"updated": "2011-01-22T13:25:27-06:00",
|
||||
"description": "The Fox In Socks Extension",
|
||||
"alias": "FOXNSOX",
|
||||
"links": []
|
||||
}
|
||||
"links": [],
|
||||
},
|
||||
)
|
||||
|
||||
def test_list_extensions_xml(self):
|
||||
@@ -176,7 +176,12 @@ class ExtensionControllerTest(unittest.TestCase):
|
||||
'The Fox In Socks Extension')
|
||||
|
||||
|
||||
class ResourceExtensionTest(unittest.TestCase):
|
||||
class ResourceExtensionTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ResourceExtensionTest, self).setUp()
|
||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||
self.flags(osapi_extensions_path=ext_path)
|
||||
|
||||
def test_no_extension_present(self):
|
||||
manager = StubExtensionManager(None)
|
||||
@@ -214,13 +219,14 @@ class InvalidExtension(object):
|
||||
return "THIRD"
|
||||
|
||||
|
||||
class ExtensionManagerTest(unittest.TestCase):
|
||||
class ExtensionManagerTest(test.TestCase):
|
||||
|
||||
response_body = "Try to say this Mr. Knox, sir..."
|
||||
|
||||
def setUp(self):
|
||||
FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
|
||||
"extensions")
|
||||
super(ExtensionManagerTest, self).setUp()
|
||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||
self.flags(osapi_extensions_path=ext_path)
|
||||
|
||||
def test_get_resources(self):
|
||||
app = openstack.APIRouterV11()
|
||||
@@ -239,11 +245,12 @@ class ExtensionManagerTest(unittest.TestCase):
|
||||
self.assertTrue('THIRD' not in ext_mgr.extensions)
|
||||
|
||||
|
||||
class ActionExtensionTest(unittest.TestCase):
|
||||
class ActionExtensionTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
|
||||
"extensions")
|
||||
super(ActionExtensionTest, self).setUp()
|
||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||
self.flags(osapi_extensions_path=ext_path)
|
||||
|
||||
def _send_server_action_request(self, url, body):
|
||||
app = openstack.APIRouterV11()
|
||||
@@ -277,19 +284,12 @@ class ActionExtensionTest(unittest.TestCase):
|
||||
self.assertEqual(404, response.status_int)
|
||||
|
||||
|
||||
class RequestExtensionTest(unittest.TestCase):
|
||||
class RequestExtensionTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(RequestExtensionTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(RequestExtensionTest, self).tearDown()
|
||||
ext_path = os.path.join(os.path.dirname(__file__), "extensions")
|
||||
self.flags(osapi_extensions_path=ext_path)
|
||||
|
||||
def test_get_resources_with_stub_mgr(self):
|
||||
|
||||
@@ -327,7 +327,7 @@ class RequestExtensionTest(unittest.TestCase):
|
||||
self.assertEqual("Pig Bands!", response_data['big_bands'])
|
||||
|
||||
|
||||
class ExtensionsXMLSerializerTest(unittest.TestCase):
|
||||
class ExtensionsXMLSerializerTest(test.TestCase):
|
||||
|
||||
def test_serialize_extenstion(self):
|
||||
serializer = extensions.ExtensionsXMLSerializer()
|
||||
@@ -342,15 +342,15 @@ class ExtensionsXMLSerializerTest(unittest.TestCase):
|
||||
{
|
||||
'rel': 'describedby',
|
||||
'type': 'application/pdf',
|
||||
'href': 'http://docs.rack.com/servers/api/ext/cs.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'
|
||||
}
|
||||
]
|
||||
}
|
||||
'href': 'http://docs.rack.com/servers/api/ext/cs.wadl',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
xml = serializer.serialize(data, 'show')
|
||||
@@ -382,14 +382,14 @@ class ExtensionsXMLSerializerTest(unittest.TestCase):
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://foo.com/api/ext/cs-pie.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"
|
||||
}
|
||||
]
|
||||
"href": "http://foo.com/api/ext/cs-pie.wadl",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Cloud Block Storage",
|
||||
@@ -401,16 +401,16 @@ class ExtensionsXMLSerializerTest(unittest.TestCase):
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://foo.com/api/ext/cs-cbs.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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"href": "http://foo.com/api/ext/cs-cbs.wadl",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
xml = serializer.serialize(data, 'index')
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import stubout
|
||||
import webob
|
||||
import xml.dom.minidom as minidom
|
||||
|
||||
@@ -56,12 +55,8 @@ def return_instance_type_not_found(context, flavor_id):
|
||||
class FlavorsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(FlavorsTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.stubs.Set(nova.db.api, "instance_type_get_all",
|
||||
return_instance_types)
|
||||
self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id",
|
||||
|
||||
+24
-42
@@ -17,14 +17,13 @@
|
||||
|
||||
import json
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
import os.path
|
||||
|
||||
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.api import openstack
|
||||
from nova.api.openstack import auth
|
||||
from nova.api.openstack import extensions
|
||||
from nova.tests.api.openstack import fakes
|
||||
import nova.wsgi
|
||||
@@ -40,10 +39,6 @@ def return_flavor_extra_specs(context, flavor_id):
|
||||
return stub_flavor_extra_specs()
|
||||
|
||||
|
||||
def return_flavor_extra_specs(context, flavor_id):
|
||||
return stub_flavor_extra_specs()
|
||||
|
||||
|
||||
def return_empty_flavor_extra_specs(context, flavor_id):
|
||||
return {}
|
||||
|
||||
@@ -62,30 +57,17 @@ def stub_flavor_extra_specs():
|
||||
return specs
|
||||
|
||||
|
||||
class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
class FlavorsExtraSpecsTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FlavorsExtraSpecsTest, self).setUp()
|
||||
FLAGS.osapi_extensions_path = os.path.join(os.path.dirname(__file__),
|
||||
"extensions")
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.auth_data = {}
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.mware = auth.AuthMiddleware(
|
||||
extensions.ExtensionMiddleware(
|
||||
openstack.APIRouterV11()))
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(FlavorsExtraSpecsTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_flavor_extra_specs)
|
||||
request = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
res = request.get_response(self.mware)
|
||||
request = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
|
||||
res = request.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
@@ -94,8 +76,8 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
def test_index_no_data(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_empty_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
res = req.get_response(self.mware)
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
@@ -104,8 +86,8 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
def test_show(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
|
||||
res = req.get_response(self.mware)
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
@@ -114,28 +96,28 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
def test_show_spec_not_found(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_get',
|
||||
return_empty_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key6')
|
||||
res = req.get_response(self.mware)
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key6')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(404, res.status_int)
|
||||
|
||||
def test_delete(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_type_extra_specs_delete',
|
||||
delete_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key5')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key5')
|
||||
req.method = 'DELETE'
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
|
||||
def test_create(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
|
||||
req.method = 'POST'
|
||||
req.body = '{"extra_specs": {"key1": "value1"}}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
@@ -145,21 +127,21 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs')
|
||||
req.method = 'POST'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, res.status_int)
|
||||
self.assertEqual('application/json', res.headers['Content-Type'])
|
||||
res_dict = json.loads(res.body)
|
||||
@@ -169,30 +151,30 @@ class FlavorsExtraSpecsTest(unittest.TestCase):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_too_many_keys(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/key1')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/key1')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1", "key2": "value2"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, res.status_int)
|
||||
|
||||
def test_update_item_body_uri_mismatch(self):
|
||||
self.stubs.Set(nova.db.api,
|
||||
'instance_type_extra_specs_update_or_create',
|
||||
return_create_flavor_extra_specs)
|
||||
req = webob.Request.blank('/flavors/1/os-extra_specs/bad')
|
||||
req = webob.Request.blank('/v1.1/flavors/1/os-extra_specs/bad')
|
||||
req.method = 'PUT'
|
||||
req.body = '{"key1": "value1"}'
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(self.mware)
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, res.status_int)
|
||||
@@ -16,10 +16,7 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
import xml.dom.minidom as minidom
|
||||
|
||||
|
||||
from nova import flags
|
||||
@@ -85,23 +82,13 @@ class ImageMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ImageMetaDataTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.orig_image_service = FLAGS.image_service
|
||||
FLAGS.image_service = 'nova.image.glance.GlanceImageService'
|
||||
fakes.FakeAuthManager.auth_data = {}
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
self.flags(image_service='nova.image.glance.GlanceImageService')
|
||||
# NOTE(dprince) max out properties/metadata in image 3 for testing
|
||||
img3 = self.IMAGE_FIXTURES[2]
|
||||
for num in range(FLAGS.quota_metadata_items):
|
||||
img3['properties']['key%i' % num] = "blah"
|
||||
fakes.stub_out_glance(self.stubs, self.IMAGE_FIXTURES)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.image_service = self.orig_image_service
|
||||
super(ImageMetaDataTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
req = webob.Request.blank('/v1.1/images/1/metadata')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
@@ -252,203 +239,3 @@ class ImageMetaDataTest(test.TestCase):
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
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(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
'one': 'two',
|
||||
'three': 'four',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="three">
|
||||
four
|
||||
</meta>
|
||||
<meta key="one">
|
||||
two
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_null(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
None: None,
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="None">
|
||||
None
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_index_unicode(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
u'three': u'Jos\xe9',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'index')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString(u"""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="three">
|
||||
Jos\xe9
|
||||
</meta>
|
||||
</metadata>
|
||||
""".encode("UTF-8").replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_show(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'meta': {
|
||||
'one': 'two',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'show')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
|
||||
two
|
||||
</meta>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
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': {
|
||||
'one': 'two',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'update')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<meta xmlns="http://docs.openstack.org/compute/api/v1.1" key="one">
|
||||
two
|
||||
</meta>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_create(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
fixture = {
|
||||
'metadata': {
|
||||
'key9': 'value9',
|
||||
'key2': 'value2',
|
||||
'key1': 'value1',
|
||||
},
|
||||
}
|
||||
output = serializer.serialize(fixture, 'create')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected = minidom.parseString("""
|
||||
<metadata xmlns="http://docs.openstack.org/compute/api/v1.1">
|
||||
<meta key="key2">
|
||||
value2
|
||||
</meta>
|
||||
<meta key="key9">
|
||||
value9
|
||||
</meta>
|
||||
<meta key="key1">
|
||||
value1
|
||||
</meta>
|
||||
</metadata>
|
||||
""".replace(" ", ""))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_delete(self):
|
||||
serializer = openstack.image_metadata.ImageMetadataXMLSerializer()
|
||||
output = serializer.serialize(None, 'delete')
|
||||
self.assertEqual(output, '')
|
||||
|
||||
@@ -155,7 +155,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
|
||||
fakes.stub_out_compute_api_snapshot(self.stubs)
|
||||
service_class = 'nova.image.glance.GlanceImageService'
|
||||
self.service = utils.import_object(service_class)
|
||||
self.context = context.RequestContext(1, None)
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
self.service.delete_all()
|
||||
self.sent_to_glance = {}
|
||||
fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance)
|
||||
@@ -168,7 +168,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
|
||||
"""Ensure instance_id is persisted as an image-property"""
|
||||
fixture = {'name': 'test image',
|
||||
'is_public': False,
|
||||
'properties': {'instance_id': '42', 'user_id': '1'}}
|
||||
'properties': {'instance_id': '42', 'user_id': 'fake'}}
|
||||
|
||||
image_id = self.service.create(self.context, fixture)['id']
|
||||
expected = fixture
|
||||
@@ -178,7 +178,7 @@ class GlanceImageServiceTest(_BaseImageServiceTests):
|
||||
expected = {'id': image_id,
|
||||
'name': 'test image',
|
||||
'is_public': False,
|
||||
'properties': {'instance_id': '42', 'user_id': '1'}}
|
||||
'properties': {'instance_id': '42', 'user_id': 'fake'}}
|
||||
self.assertDictMatch(image_meta, expected)
|
||||
|
||||
image_metas = self.service.detail(self.context)
|
||||
@@ -331,11 +331,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
self.orig_image_service = FLAGS.image_service
|
||||
FLAGS.image_service = 'nova.image.glance.GlanceImageService'
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.fixtures = self._make_image_fixtures()
|
||||
fakes.stub_out_glance(self.stubs, initial_fixtures=self.fixtures)
|
||||
@@ -352,7 +349,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
"""Determine if this fixture is applicable for given user id."""
|
||||
is_public = fixture["is_public"]
|
||||
try:
|
||||
uid = int(fixture["properties"]["user_id"])
|
||||
uid = fixture["properties"]["user_id"]
|
||||
except KeyError:
|
||||
uid = None
|
||||
return uid == user_id or is_public
|
||||
@@ -424,7 +421,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
},
|
||||
"metadata": {
|
||||
"instance_ref": "http://localhost/v1.1/servers/42",
|
||||
"user_id": "1",
|
||||
"user_id": "fake",
|
||||
},
|
||||
"links": [{
|
||||
"rel": "self",
|
||||
@@ -559,7 +556,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
fixtures = copy.copy(self.fixtures)
|
||||
|
||||
for image in fixtures:
|
||||
if not self._applicable_fixture(image, 1):
|
||||
if not self._applicable_fixture(image, "fake"):
|
||||
fixtures.remove(image)
|
||||
continue
|
||||
|
||||
@@ -666,7 +663,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
'name': 'queued snapshot',
|
||||
'metadata': {
|
||||
u'instance_ref': u'http://localhost/v1.1/servers/42',
|
||||
u'user_id': u'1',
|
||||
u'user_id': u'fake',
|
||||
},
|
||||
'updated': self.NOW_API_FORMAT,
|
||||
'created': self.NOW_API_FORMAT,
|
||||
@@ -696,7 +693,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
'name': 'saving snapshot',
|
||||
'metadata': {
|
||||
u'instance_ref': u'http://localhost/v1.1/servers/42',
|
||||
u'user_id': u'1',
|
||||
u'user_id': u'fake',
|
||||
},
|
||||
'updated': self.NOW_API_FORMAT,
|
||||
'created': self.NOW_API_FORMAT,
|
||||
@@ -727,7 +724,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
'name': 'active snapshot',
|
||||
'metadata': {
|
||||
u'instance_ref': u'http://localhost/v1.1/servers/42',
|
||||
u'user_id': u'1',
|
||||
u'user_id': u'fake',
|
||||
},
|
||||
'updated': self.NOW_API_FORMAT,
|
||||
'created': self.NOW_API_FORMAT,
|
||||
@@ -757,7 +754,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
'name': 'killed snapshot',
|
||||
'metadata': {
|
||||
u'instance_ref': u'http://localhost/v1.1/servers/42',
|
||||
u'user_id': u'1',
|
||||
u'user_id': u'fake',
|
||||
},
|
||||
'updated': self.NOW_API_FORMAT,
|
||||
'created': self.NOW_API_FORMAT,
|
||||
@@ -1045,82 +1042,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_backup_no_name(self):
|
||||
"""Name is also required for backups"""
|
||||
body = dict(image=dict(serverId='123', image_type='backup',
|
||||
backup_type='daily', rotation=1))
|
||||
req = webob.Request.blank('/v1.0/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_backup_with_rotation_and_backup_type(self):
|
||||
"""The happy path for creating backups
|
||||
|
||||
Creating a backup is an admin-only operation, as opposed to snapshots
|
||||
which are available to anybody.
|
||||
"""
|
||||
# FIXME(sirp): teardown needed?
|
||||
FLAGS.allow_admin_api = True
|
||||
|
||||
# FIXME(sirp): should the fact that backups are admin_only be a FLAG
|
||||
body = dict(image=dict(serverId='123', image_type='backup',
|
||||
name='Backup 1',
|
||||
backup_type='daily', rotation=1))
|
||||
req = webob.Request.blank('/v1.0/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
def test_create_backup_no_rotation(self):
|
||||
"""Rotation is required for backup requests"""
|
||||
# FIXME(sirp): teardown needed?
|
||||
FLAGS.allow_admin_api = True
|
||||
|
||||
# FIXME(sirp): should the fact that backups are admin_only be a FLAG
|
||||
body = dict(image=dict(serverId='123', name='daily',
|
||||
image_type='backup', backup_type='daily'))
|
||||
req = webob.Request.blank('/v1.0/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_backup_no_backup_type(self):
|
||||
"""Backup Type (daily or weekly) is required for backup requests"""
|
||||
# FIXME(sirp): teardown needed?
|
||||
FLAGS.allow_admin_api = True
|
||||
|
||||
# FIXME(sirp): should the fact that backups are admin_only be a FLAG
|
||||
body = dict(image=dict(serverId='123', name='daily',
|
||||
image_type='backup', rotation=1))
|
||||
req = webob.Request.blank('/v1.0/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_with_invalid_image_type(self):
|
||||
"""Valid image_types are snapshot | daily | weekly"""
|
||||
# FIXME(sirp): teardown needed?
|
||||
FLAGS.allow_admin_api = True
|
||||
|
||||
# FIXME(sirp): should the fact that backups are admin_only be a FLAG
|
||||
body = dict(image=dict(serverId='123', image_type='monthly',
|
||||
rotation=1))
|
||||
req = webob.Request.blank('/v1.0/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_no_server_id(self):
|
||||
|
||||
body = dict(image=dict(name='Snapshot 1'))
|
||||
@@ -1131,113 +1052,6 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_v1_1(self):
|
||||
|
||||
body = dict(image=dict(serverRef='123', name='Snapshot 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
def test_create_image_v1_1_actual_server_ref(self):
|
||||
|
||||
serverRef = 'http://localhost/v1.1/servers/1'
|
||||
serverBookmark = 'http://localhost/servers/1'
|
||||
body = dict(image=dict(serverRef=serverRef, name='Backup 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, response.status_int)
|
||||
result = json.loads(response.body)
|
||||
expected = {
|
||||
'id': 1,
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': serverRef,
|
||||
},
|
||||
{
|
||||
'rel': 'bookmark',
|
||||
'href': serverBookmark,
|
||||
},
|
||||
]
|
||||
}
|
||||
self.assertEqual(result['image']['server'], expected)
|
||||
|
||||
def test_create_image_v1_1_actual_server_ref_port(self):
|
||||
|
||||
serverRef = 'http://localhost:8774/v1.1/servers/1'
|
||||
serverBookmark = 'http://localhost:8774/servers/1'
|
||||
body = dict(image=dict(serverRef=serverRef, name='Backup 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(200, response.status_int)
|
||||
result = json.loads(response.body)
|
||||
expected = {
|
||||
'id': 1,
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': serverRef,
|
||||
},
|
||||
{
|
||||
'rel': 'bookmark',
|
||||
'href': serverBookmark,
|
||||
},
|
||||
]
|
||||
}
|
||||
self.assertEqual(result['image']['server'], expected)
|
||||
|
||||
def test_create_image_v1_1_server_ref_bad_hostname(self):
|
||||
|
||||
serverRef = 'http://asdf/v1.1/servers/1'
|
||||
body = dict(image=dict(serverRef=serverRef, name='Backup 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_v1_1_no_server_ref(self):
|
||||
|
||||
body = dict(image=dict(name='Snapshot 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_v1_1_server_ref_missing_version(self):
|
||||
|
||||
serverRef = 'http://localhost/servers/1'
|
||||
body = dict(image=dict(serverRef=serverRef, name='Backup 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
def test_create_image_v1_1_server_ref_missing_id(self):
|
||||
|
||||
serverRef = 'http://localhost/v1.1/servers'
|
||||
body = dict(image=dict(serverRef=serverRef, name='Backup 1'))
|
||||
req = webob.Request.blank('/v1.1/images')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
response = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(400, response.status_int)
|
||||
|
||||
@classmethod
|
||||
def _make_image_fixtures(cls):
|
||||
image_id = 123
|
||||
@@ -1259,7 +1073,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
|
||||
# Snapshot for User 1
|
||||
server_ref = 'http://localhost/v1.1/servers/42'
|
||||
snapshot_properties = {'instance_ref': server_ref, 'user_id': '1'}
|
||||
snapshot_properties = {'instance_ref': server_ref, 'user_id': 'fake'}
|
||||
for status in ('queued', 'saving', 'active', 'killed'):
|
||||
add_fixture(id=image_id, name='%s snapshot' % status,
|
||||
is_public=False, status=status,
|
||||
@@ -1267,7 +1081,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
|
||||
image_id += 1
|
||||
|
||||
# Snapshot for User 2
|
||||
other_snapshot_properties = {'instance_id': '43', 'user_id': '2'}
|
||||
other_snapshot_properties = {'instance_id': '43', 'user_id': 'other'}
|
||||
add_fixture(id=image_id, name='someone elses snapshot',
|
||||
is_public=False, status='active',
|
||||
properties=other_snapshot_properties)
|
||||
@@ -1716,76 +1530,3 @@ class ImageXMLSerializationTest(test.TestCase):
|
||||
""".replace(" ", "") % (locals()))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
def test_create(self):
|
||||
serializer = images.ImageXMLSerializer()
|
||||
|
||||
fixture = {
|
||||
'image': {
|
||||
'id': 1,
|
||||
'name': 'Image1',
|
||||
'created': self.TIMESTAMP,
|
||||
'updated': self.TIMESTAMP,
|
||||
'status': 'SAVING',
|
||||
'progress': 80,
|
||||
'server': {
|
||||
'id': 1,
|
||||
'links': [
|
||||
{
|
||||
'href': self.SERVER_HREF,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.SERVER_BOOKMARK,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
'metadata': {
|
||||
'key1': 'value1',
|
||||
},
|
||||
'links': [
|
||||
{
|
||||
'href': self.IMAGE_HREF % 1,
|
||||
'rel': 'self',
|
||||
},
|
||||
{
|
||||
'href': self.IMAGE_BOOKMARK % 1,
|
||||
'rel': 'bookmark',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
output = serializer.serialize(fixture, 'create')
|
||||
actual = minidom.parseString(output.replace(" ", ""))
|
||||
|
||||
expected_server_href = self.SERVER_HREF
|
||||
expected_server_bookmark = self.SERVER_BOOKMARK
|
||||
expected_href = self.IMAGE_HREF % 1
|
||||
expected_bookmark = self.IMAGE_BOOKMARK % 1
|
||||
expected_now = self.TIMESTAMP
|
||||
expected = minidom.parseString("""
|
||||
<image id="1"
|
||||
xmlns="http://docs.openstack.org/compute/api/v1.1"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||
name="Image1"
|
||||
updated="%(expected_now)s"
|
||||
created="%(expected_now)s"
|
||||
status="SAVING"
|
||||
progress="80">
|
||||
<server id="1">
|
||||
<atom:link rel="self" href="%(expected_server_href)s"/>
|
||||
<atom:link rel="bookmark" href="%(expected_server_bookmark)s"/>
|
||||
</server>
|
||||
<metadata>
|
||||
<meta key="key1">
|
||||
value1
|
||||
</meta>
|
||||
</metadata>
|
||||
<atom:link href="%(expected_href)s" rel="self"/>
|
||||
<atom:link href="%(expected_bookmark)s" rel="bookmark"/>
|
||||
</image>
|
||||
""".replace(" ", "") % (locals()))
|
||||
|
||||
self.assertEqual(expected.toxml(), actual.toxml())
|
||||
|
||||
@@ -920,7 +920,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
|
||||
"verb": "POST",
|
||||
"remaining": 2,
|
||||
"unit": "MINUTE",
|
||||
"resetTime": 1311272226
|
||||
"resetTime": 1311272226,
|
||||
},
|
||||
{
|
||||
"URI": "*/servers",
|
||||
@@ -929,7 +929,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
|
||||
"verb": "POST",
|
||||
"remaining": 10,
|
||||
"unit": "DAY",
|
||||
"resetTime": 1311272226
|
||||
"resetTime": 1311272226,
|
||||
},
|
||||
]
|
||||
self.absolute_limits = {
|
||||
@@ -954,7 +954,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
|
||||
"verb": "POST",
|
||||
"remaining": 2,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2011-07-21T18:17:06Z"
|
||||
"next-available": "2011-07-21T18:17:06Z",
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -967,7 +967,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
|
||||
"verb": "POST",
|
||||
"remaining": 10,
|
||||
"unit": "DAY",
|
||||
"next-available": "2011-07-21T18:17:06Z"
|
||||
"next-available": "2011-07-21T18:17:06Z",
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -989,7 +989,7 @@ class LimitsViewBuilderV11Test(test.TestCase):
|
||||
expected_limits = {
|
||||
"limits": {
|
||||
"rate": [],
|
||||
"absolute": {}
|
||||
"absolute": {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,7 +1022,7 @@ class LimitsXMLSerializationTest(test.TestCase):
|
||||
"verb": "POST",
|
||||
"remaining": 2,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2011-12-15T22:42:45Z"
|
||||
"next-available": "2011-12-15T22:42:45Z",
|
||||
},
|
||||
]
|
||||
},
|
||||
@@ -1083,7 +1083,7 @@ class LimitsXMLSerializationTest(test.TestCase):
|
||||
fixture = {
|
||||
"limits": {
|
||||
"rate": [],
|
||||
"absolute": {}
|
||||
"absolute": {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,12 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import stubout
|
||||
import unittest
|
||||
import webob
|
||||
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.api import openstack
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
import nova.wsgi
|
||||
|
||||
@@ -76,21 +74,13 @@ def return_server_nonexistant(context, server_id):
|
||||
raise exception.InstanceNotFound()
|
||||
|
||||
|
||||
class ServerMetaDataTest(unittest.TestCase):
|
||||
class ServerMetaDataTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ServerMetaDataTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.auth_data = {}
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
fakes.stub_out_key_pair_funcs(self.stubs)
|
||||
self.stubs.Set(nova.db.api, 'instance_get', return_server)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(ServerMetaDataTest, self).tearDown()
|
||||
|
||||
def test_index(self):
|
||||
self.stubs.Set(nova.db.api, 'instance_metadata_get',
|
||||
return_server_metadata)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,26 +15,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from nova import test
|
||||
from nova.api.openstack import shared_ip_groups
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
class SharedIpGroupsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(SharedIpGroupsTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
super(SharedIpGroupsTest, self).tearDown()
|
||||
|
||||
def test_get_shared_ip_groups(self):
|
||||
req = webob.Request.blank('/v1.0/shared_ip_groups')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
import json
|
||||
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from nova import flags
|
||||
@@ -41,7 +40,7 @@ def fake_admin_check(self, req):
|
||||
class UsersTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(UsersTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.flags(allow_admin_api=True)
|
||||
self.stubs.Set(users.Controller, '__init__',
|
||||
fake_init)
|
||||
self.stubs.Set(users.Controller, '_check_admin',
|
||||
@@ -58,16 +57,10 @@ class UsersTest(test.TestCase):
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
|
||||
self.allow_admin = FLAGS.allow_admin_api
|
||||
FLAGS.allow_admin_api = True
|
||||
fakemgr = fakes.FakeAuthManager()
|
||||
fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False))
|
||||
fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True))
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.allow_admin_api = self.allow_admin
|
||||
super(UsersTest, self).tearDown()
|
||||
|
||||
def test_get_user_list(self):
|
||||
req = webob.Request.blank('/v1.0/users')
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
|
||||
@@ -16,21 +16,92 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import stubout
|
||||
import webob
|
||||
import xml.etree.ElementTree
|
||||
|
||||
|
||||
from nova import context
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import fakes
|
||||
from nova.api.openstack import versions
|
||||
from nova.api.openstack import views
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
VERSIONS = {
|
||||
"v1.0": {
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.0+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
"v1.1": {
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.1+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class VersionsTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(VersionsTest, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
#Stub out VERSIONS
|
||||
self.old_versions = versions.VERSIONS
|
||||
versions.VERSIONS = VERSIONS
|
||||
|
||||
def tearDown(self):
|
||||
versions.VERSIONS = self.old_versions
|
||||
super(VersionsTest, self).tearDown()
|
||||
|
||||
def test_get_version_list(self):
|
||||
@@ -44,7 +115,7 @@ class VersionsTest(test.TestCase):
|
||||
{
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
@@ -54,7 +125,7 @@ class VersionsTest(test.TestCase):
|
||||
{
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"updated": "2010-10-09T11:30:00Z",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
@@ -64,6 +135,183 @@ class VersionsTest(test.TestCase):
|
||||
]
|
||||
self.assertEqual(versions, expected)
|
||||
|
||||
def test_get_version_1_0_detail(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
version = json.loads(res.body)
|
||||
expected = {
|
||||
"version": {
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.0/"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/application.wadl"
|
||||
}
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute-v1.0+json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected, version)
|
||||
|
||||
def test_get_version_1_1_detail(self):
|
||||
req = webob.Request.blank('/v1.1/')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
version = json.loads(res.body)
|
||||
expected = {
|
||||
"version": {
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.1/"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/application.wadl"
|
||||
}
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute-v1.1+json"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected, version)
|
||||
|
||||
def test_get_version_1_0_detail_xml(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.accept = "application/xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/xml")
|
||||
root = xml.etree.ElementTree.XML(res.body)
|
||||
self.assertEqual(root.tag.split('}')[1], "version")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
|
||||
|
||||
children = list(root)
|
||||
media_types = children[0]
|
||||
media_type_nodes = list(media_types)
|
||||
links = (children[1], children[2], children[3])
|
||||
|
||||
self.assertEqual(media_types.tag.split('}')[1], 'media-types')
|
||||
for media_node in media_type_nodes:
|
||||
self.assertEqual(media_node.tag.split('}')[1], 'media-type')
|
||||
|
||||
expected = """
|
||||
<version id="v1.0" status="DEPRECATED"
|
||||
updated="2011-01-21T11:33:21Z"
|
||||
xmlns="%s"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
|
||||
<media-types>
|
||||
<media-type base="application/xml"
|
||||
type="application/vnd.openstack.compute-v1.0+xml"/>
|
||||
<media-type base="application/json"
|
||||
type="application/vnd.openstack.compute-v1.0+json"/>
|
||||
</media-types>
|
||||
|
||||
<atom:link href="http://localhost/v1.0/"
|
||||
rel="self"/>
|
||||
|
||||
<atom:link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.0/cs-devguide-20110125.pdf"
|
||||
rel="describedby"
|
||||
type="application/pdf"/>
|
||||
|
||||
<atom:link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.0/application.wadl"
|
||||
rel="describedby"
|
||||
type="application/vnd.sun.wadl+xml"/>
|
||||
</version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11
|
||||
|
||||
actual = res.body.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_version_1_1_detail_xml(self):
|
||||
req = webob.Request.blank('/v1.1/')
|
||||
req.accept = "application/xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/xml")
|
||||
expected = """
|
||||
<version id="v1.1" status="CURRENT"
|
||||
updated="2011-01-21T11:33:21Z"
|
||||
xmlns="%s"
|
||||
xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
|
||||
<media-types>
|
||||
<media-type base="application/xml"
|
||||
type="application/vnd.openstack.compute-v1.1+xml"/>
|
||||
<media-type base="application/json"
|
||||
type="application/vnd.openstack.compute-v1.1+json"/>
|
||||
</media-types>
|
||||
|
||||
<atom:link href="http://localhost/v1.1/"
|
||||
rel="self"/>
|
||||
|
||||
<atom:link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.1/cs-devguide-20110125.pdf"
|
||||
rel="describedby"
|
||||
type="application/pdf"/>
|
||||
|
||||
<atom:link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.1/application.wadl"
|
||||
rel="describedby"
|
||||
type="application/vnd.sun.wadl+xml"/>
|
||||
</version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11
|
||||
|
||||
actual = res.body.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_version_list_xml(self):
|
||||
req = webob.Request.blank('/')
|
||||
req.accept = "application/xml"
|
||||
@@ -71,20 +319,96 @@ class VersionsTest(test.TestCase):
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/xml")
|
||||
|
||||
expected = """<versions>
|
||||
<version id="v1.1" status="CURRENT" updated="2011-07-18T11:30:00Z">
|
||||
expected = """
|
||||
<versions xmlns="%s" xmlns:atom="%s">
|
||||
<version id="v1.1" status="CURRENT" updated="2011-01-21T11:33:21Z">
|
||||
<atom:link href="http://localhost/v1.1/" rel="self"/>
|
||||
</version>
|
||||
<version id="v1.0" status="DEPRECATED"
|
||||
updated="2010-10-09T11:30:00Z">
|
||||
updated="2011-01-21T11:33:21Z">
|
||||
<atom:link href="http://localhost/v1.0/" rel="self"/>
|
||||
</version>
|
||||
</versions>""".replace(" ", "").replace("\n", "")
|
||||
</versions>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11,
|
||||
wsgi.XMLNS_ATOM)
|
||||
|
||||
actual = res.body.replace(" ", "").replace("\n", "")
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_version_1_0_detail_atom(self):
|
||||
req = webob.Request.blank('/v1.0/')
|
||||
req.accept = "application/atom+xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual("application/atom+xml", res.content_type)
|
||||
expected = """
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title type="text">About This Version</title>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<id>http://localhost/v1.0/</id>
|
||||
<author>
|
||||
<name>Rackspace</name>
|
||||
<uri>http://www.rackspace.com/</uri>
|
||||
</author>
|
||||
<link href="http://localhost/v1.0/" rel="self"/>
|
||||
<entry>
|
||||
<id>http://localhost/v1.0/</id>
|
||||
<title type="text">Version v1.0</title>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<link href="http://localhost/v1.0/"
|
||||
rel="self"/>
|
||||
<link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.0/cs-devguide-20110125.pdf"
|
||||
rel="describedby" type="application/pdf"/>
|
||||
<link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.0/application.wadl"
|
||||
rel="describedby" type="application/vnd.sun.wadl+xml"/>
|
||||
<content type="text">
|
||||
Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)
|
||||
</content>
|
||||
</entry>
|
||||
</feed>""".replace(" ", "").replace("\n", "")
|
||||
|
||||
actual = res.body.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_version_1_1_detail_atom(self):
|
||||
req = webob.Request.blank('/v1.1/')
|
||||
req.accept = "application/atom+xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual("application/atom+xml", res.content_type)
|
||||
expected = """
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title type="text">About This Version</title>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<id>http://localhost/v1.1/</id>
|
||||
<author>
|
||||
<name>Rackspace</name>
|
||||
<uri>http://www.rackspace.com/</uri>
|
||||
</author>
|
||||
<link href="http://localhost/v1.1/" rel="self"/>
|
||||
<entry>
|
||||
<id>http://localhost/v1.1/</id>
|
||||
<title type="text">Version v1.1</title>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<link href="http://localhost/v1.1/"
|
||||
rel="self"/>
|
||||
<link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.1/cs-devguide-20110125.pdf"
|
||||
rel="describedby" type="application/pdf"/>
|
||||
<link href="http://docs.rackspacecloud.com/servers/
|
||||
api/v1.1/application.wadl"
|
||||
rel="describedby" type="application/vnd.sun.wadl+xml"/>
|
||||
<content type="text">
|
||||
Version v1.1 CURRENT (2011-01-21T11:33:21Z)
|
||||
</content>
|
||||
</entry>
|
||||
</feed>""".replace(" ", "").replace("\n", "")
|
||||
|
||||
actual = res.body.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_get_version_list_atom(self):
|
||||
req = webob.Request.blank('/')
|
||||
req.accept = "application/atom+xml"
|
||||
@@ -95,7 +419,7 @@ class VersionsTest(test.TestCase):
|
||||
expected = """
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title type="text">Available API Versions</title>
|
||||
<updated>2011-07-18T11:30:00Z</updated>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<id>http://localhost/</id>
|
||||
<author>
|
||||
<name>Rackspace</name>
|
||||
@@ -105,19 +429,19 @@ class VersionsTest(test.TestCase):
|
||||
<entry>
|
||||
<id>http://localhost/v1.1/</id>
|
||||
<title type="text">Version v1.1</title>
|
||||
<updated>2011-07-18T11:30:00Z</updated>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<link href="http://localhost/v1.1/" rel="self"/>
|
||||
<content type="text">
|
||||
Version v1.1 CURRENT (2011-07-18T11:30:00Z)
|
||||
Version v1.1 CURRENT (2011-01-21T11:33:21Z)
|
||||
</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<id>http://localhost/v1.0/</id>
|
||||
<title type="text">Version v1.0</title>
|
||||
<updated>2010-10-09T11:30:00Z</updated>
|
||||
<updated>2011-01-21T11:33:21Z</updated>
|
||||
<link href="http://localhost/v1.0/" rel="self"/>
|
||||
<content type="text">
|
||||
Version v1.0 DEPRECATED (2010-10-09T11:30:00Z)
|
||||
Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)
|
||||
</content>
|
||||
</entry>
|
||||
</feed>
|
||||
@@ -127,28 +451,184 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_multi_choice_image(self):
|
||||
req = webob.Request.blank('/images/1')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 300)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
|
||||
expected = {
|
||||
"choices": [
|
||||
{
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v1.1/images/1",
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.1+json"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v1.0/images/1",
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.0+json"
|
||||
},
|
||||
],
|
||||
},
|
||||
], }
|
||||
|
||||
self.assertDictMatch(expected, json.loads(res.body))
|
||||
|
||||
def test_multi_choice_image_xml(self):
|
||||
req = webob.Request.blank('/images/1')
|
||||
req.accept = "application/xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 300)
|
||||
self.assertEqual(res.content_type, "application/xml")
|
||||
|
||||
expected = """
|
||||
<choices xmlns="%s" xmlns:atom="%s">
|
||||
<version id="v1.1" status="CURRENT">
|
||||
<media-types>
|
||||
<media-type base="application/xml"
|
||||
type="application/vnd.openstack.compute-v1.1+xml"/>
|
||||
<media-type base="application/json"
|
||||
type="application/vnd.openstack.compute-v1.1+json"/>
|
||||
</media-types>
|
||||
<atom:link href="http://localhost/v1.1/images/1" rel="self"/>
|
||||
</version>
|
||||
<version id="v1.0" status="DEPRECATED">
|
||||
<media-types>
|
||||
<media-type base="application/xml"
|
||||
type="application/vnd.openstack.compute-v1.0+xml"/>
|
||||
<media-type base="application/json"
|
||||
type="application/vnd.openstack.compute-v1.0+json"/>
|
||||
</media-types>
|
||||
<atom:link href="http://localhost/v1.0/images/1" rel="self"/>
|
||||
</version>
|
||||
</choices>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11,
|
||||
wsgi.XMLNS_ATOM)
|
||||
|
||||
def test_multi_choice_server_atom(self):
|
||||
"""
|
||||
Make sure multi choice responses do not have content-type
|
||||
application/atom+xml (should use default of json)
|
||||
"""
|
||||
req = webob.Request.blank('/servers/2')
|
||||
req.accept = "application/atom+xml"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 300)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
|
||||
def test_multi_choice_server(self):
|
||||
req = webob.Request.blank('/servers/2')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app())
|
||||
self.assertEqual(res.status_int, 300)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
|
||||
expected = {
|
||||
"choices": [
|
||||
{
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v1.1/servers/2",
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.1+json"
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "v1.0",
|
||||
"status": "DEPRECATED",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v1.0/servers/2",
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.0+json"
|
||||
},
|
||||
],
|
||||
},
|
||||
], }
|
||||
|
||||
self.assertDictMatch(expected, json.loads(res.body))
|
||||
|
||||
|
||||
class VersionsViewBuilderTests(test.TestCase):
|
||||
def test_view_builder(self):
|
||||
base_url = "http://example.org/"
|
||||
|
||||
version_data = {
|
||||
"id": "3.2.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-07-18T11:30:00Z"}
|
||||
"v3.2.1": {
|
||||
"id": "3.2.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
}
|
||||
}
|
||||
|
||||
expected = {
|
||||
"id": "3.2.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
"links": [
|
||||
"versions": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://example.org/3.2.1/",
|
||||
},
|
||||
],
|
||||
"id": "3.2.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://example.org/3.2.1/",
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
builder = views.versions.ViewBuilder(base_url)
|
||||
output = builder.build(version_data)
|
||||
output = builder.build_versions(version_data)
|
||||
|
||||
self.assertEqual(output, expected)
|
||||
|
||||
@@ -163,7 +643,9 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_xml_serializer(self):
|
||||
|
||||
class VersionsSerializerTests(test.TestCase):
|
||||
def test_versions_list_xml_serializer(self):
|
||||
versions_data = {
|
||||
'versions': [
|
||||
{
|
||||
@@ -180,20 +662,137 @@ class VersionsTest(test.TestCase):
|
||||
]
|
||||
}
|
||||
|
||||
expected = """
|
||||
<versions>
|
||||
<version id="2.7.1" status="DEPRECATED"
|
||||
updated="2011-07-18T11:30:00Z">
|
||||
<atom:link href="http://test/2.7.1" rel="self"/>
|
||||
</version>
|
||||
</versions>""".replace(" ", "").replace("\n", "")
|
||||
serializer = versions.VersionsXMLSerializer()
|
||||
response = serializer.index(versions_data)
|
||||
|
||||
root = xml.etree.ElementTree.XML(response)
|
||||
self.assertEqual(root.tag.split('}')[1], "versions")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
|
||||
version = list(root)[0]
|
||||
self.assertEqual(version.tag.split('}')[1], "version")
|
||||
self.assertEqual(version.get('id'),
|
||||
versions_data['versions'][0]['id'])
|
||||
self.assertEqual(version.get('status'),
|
||||
versions_data['versions'][0]['status'])
|
||||
|
||||
link = list(version)[0]
|
||||
|
||||
self.assertEqual(link.tag.split('}')[1], "link")
|
||||
self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM)
|
||||
for key, val in versions_data['versions'][0]['links'][0].items():
|
||||
self.assertEqual(link.get(key), val)
|
||||
|
||||
def test_versions_multi_xml_serializer(self):
|
||||
versions_data = {
|
||||
'choices': [
|
||||
{
|
||||
"id": "2.7.1",
|
||||
"updated": "2011-07-18T11:30:00Z",
|
||||
"status": "DEPRECATED",
|
||||
"media-types": VERSIONS['v1.1']['media-types'],
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://test/2.7.1/images",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
serializer = versions.VersionsXMLSerializer()
|
||||
response = serializer.default(versions_data)
|
||||
response = response.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, response)
|
||||
response = serializer.multi(versions_data)
|
||||
|
||||
def test_atom_serializer(self):
|
||||
root = xml.etree.ElementTree.XML(response)
|
||||
self.assertEqual(root.tag.split('}')[1], "choices")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
|
||||
version = list(root)[0]
|
||||
self.assertEqual(version.tag.split('}')[1], "version")
|
||||
self.assertEqual(version.get('id'), versions_data['choices'][0]['id'])
|
||||
self.assertEqual(version.get('status'),
|
||||
versions_data['choices'][0]['status'])
|
||||
|
||||
media_types = list(version)[0]
|
||||
media_type_nodes = list(media_types)
|
||||
self.assertEqual(media_types.tag.split('}')[1], "media-types")
|
||||
|
||||
set_types = versions_data['choices'][0]['media-types']
|
||||
for i, type in enumerate(set_types):
|
||||
node = media_type_nodes[i]
|
||||
self.assertEqual(node.tag.split('}')[1], "media-type")
|
||||
for key, val in set_types[i].items():
|
||||
self.assertEqual(node.get(key), val)
|
||||
|
||||
link = list(version)[1]
|
||||
|
||||
self.assertEqual(link.tag.split('}')[1], "link")
|
||||
self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM)
|
||||
for key, val in versions_data['choices'][0]['links'][0].items():
|
||||
self.assertEqual(link.get(key), val)
|
||||
|
||||
def test_version_detail_xml_serializer(self):
|
||||
version_data = {
|
||||
"version": {
|
||||
"id": "v1.0",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.0/"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.0/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.0+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.0+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
serializer = versions.VersionsXMLSerializer()
|
||||
response = serializer.show(version_data)
|
||||
|
||||
root = xml.etree.ElementTree.XML(response)
|
||||
self.assertEqual(root.tag.split('}')[1], "version")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
|
||||
|
||||
children = list(root)
|
||||
media_types = children[0]
|
||||
media_type_nodes = list(media_types)
|
||||
links = (children[1], children[2], children[3])
|
||||
|
||||
self.assertEqual(media_types.tag.split('}')[1], 'media-types')
|
||||
for i, media_node in enumerate(media_type_nodes):
|
||||
self.assertEqual(media_node.tag.split('}')[1], 'media-type')
|
||||
for key, val in version_data['version']['media-types'][i].items():
|
||||
self.assertEqual(val, media_node.get(key))
|
||||
|
||||
for i, link in enumerate(links):
|
||||
self.assertEqual(link.tag.split('}')[0].strip('{'),
|
||||
'http://www.w3.org/2005/Atom')
|
||||
self.assertEqual(link.tag.split('}')[1], 'link')
|
||||
for key, val in version_data['version']['links'][i].items():
|
||||
self.assertEqual(val, link.get(key))
|
||||
|
||||
def test_versions_list_atom_serializer(self):
|
||||
versions_data = {
|
||||
'versions': [
|
||||
{
|
||||
@@ -210,45 +809,158 @@ class VersionsTest(test.TestCase):
|
||||
]
|
||||
}
|
||||
|
||||
expected = """
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title type="text">
|
||||
Available API Versions
|
||||
</title>
|
||||
<updated>
|
||||
2011-07-20T11:40:00Z
|
||||
</updated>
|
||||
<id>
|
||||
http://test/
|
||||
</id>
|
||||
<author>
|
||||
<name>
|
||||
Rackspace
|
||||
</name>
|
||||
<uri>
|
||||
http://www.rackspace.com/
|
||||
</uri>
|
||||
</author>
|
||||
<link href="http://test/" rel="self"/>
|
||||
<entry>
|
||||
<id>
|
||||
http://test/2.9.8
|
||||
</id>
|
||||
<title type="text">
|
||||
Version 2.9.8
|
||||
</title>
|
||||
<updated>
|
||||
2011-07-20T11:40:00Z
|
||||
</updated>
|
||||
<link href="http://test/2.9.8" rel="self"/>
|
||||
<content type="text">
|
||||
Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)
|
||||
</content>
|
||||
</entry>
|
||||
</feed>""".replace(" ", "").replace("\n", "")
|
||||
serializer = versions.VersionsAtomSerializer()
|
||||
response = serializer.index(versions_data)
|
||||
|
||||
root = xml.etree.ElementTree.XML(response)
|
||||
self.assertEqual(root.tag.split('}')[1], "feed")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'),
|
||||
"http://www.w3.org/2005/Atom")
|
||||
|
||||
children = list(root)
|
||||
title = children[0]
|
||||
updated = children[1]
|
||||
id = children[2]
|
||||
author = children[3]
|
||||
link = children[4]
|
||||
entry = children[5]
|
||||
|
||||
self.assertEqual(title.tag.split('}')[1], 'title')
|
||||
self.assertEqual(title.text, 'Available API Versions')
|
||||
self.assertEqual(updated.tag.split('}')[1], 'updated')
|
||||
self.assertEqual(updated.text, '2011-07-20T11:40:00Z')
|
||||
self.assertEqual(id.tag.split('}')[1], 'id')
|
||||
self.assertEqual(id.text, 'http://test/')
|
||||
|
||||
self.assertEqual(author.tag.split('}')[1], 'author')
|
||||
author_name = list(author)[0]
|
||||
author_uri = list(author)[1]
|
||||
self.assertEqual(author_name.tag.split('}')[1], 'name')
|
||||
self.assertEqual(author_name.text, 'Rackspace')
|
||||
self.assertEqual(author_uri.tag.split('}')[1], 'uri')
|
||||
self.assertEqual(author_uri.text, 'http://www.rackspace.com/')
|
||||
|
||||
self.assertEqual(link.get('href'), 'http://test/')
|
||||
self.assertEqual(link.get('rel'), 'self')
|
||||
|
||||
self.assertEqual(entry.tag.split('}')[1], 'entry')
|
||||
entry_children = list(entry)
|
||||
entry_id = entry_children[0]
|
||||
entry_title = entry_children[1]
|
||||
entry_updated = entry_children[2]
|
||||
entry_link = entry_children[3]
|
||||
entry_content = entry_children[4]
|
||||
self.assertEqual(entry_id.tag.split('}')[1], "id")
|
||||
self.assertEqual(entry_id.text, "http://test/2.9.8")
|
||||
self.assertEqual(entry_title.tag.split('}')[1], "title")
|
||||
self.assertEqual(entry_title.get('type'), "text")
|
||||
self.assertEqual(entry_title.text, "Version 2.9.8")
|
||||
self.assertEqual(entry_updated.tag.split('}')[1], "updated")
|
||||
self.assertEqual(entry_updated.text, "2011-07-20T11:40:00Z")
|
||||
self.assertEqual(entry_link.tag.split('}')[1], "link")
|
||||
self.assertEqual(entry_link.get('href'), "http://test/2.9.8")
|
||||
self.assertEqual(entry_link.get('rel'), "self")
|
||||
self.assertEqual(entry_content.tag.split('}')[1], "content")
|
||||
self.assertEqual(entry_content.get('type'), "text")
|
||||
self.assertEqual(entry_content.text,
|
||||
"Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)")
|
||||
|
||||
def test_version_detail_atom_serializer(self):
|
||||
versions_data = {
|
||||
"version": {
|
||||
"id": "v1.1",
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v1.1/"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/cs-devguide-20110125.pdf"
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"servers/api/v1.1/application.wadl"
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute-v1.1+xml"
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute-v1.1+json"
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
serializer = versions.VersionsAtomSerializer()
|
||||
response = serializer.default(versions_data)
|
||||
print response
|
||||
response = response.replace(" ", "").replace("\n", "")
|
||||
self.assertEqual(expected, response)
|
||||
response = serializer.show(versions_data)
|
||||
|
||||
root = xml.etree.ElementTree.XML(response)
|
||||
self.assertEqual(root.tag.split('}')[1], "feed")
|
||||
self.assertEqual(root.tag.split('}')[0].strip('{'),
|
||||
"http://www.w3.org/2005/Atom")
|
||||
|
||||
children = list(root)
|
||||
title = children[0]
|
||||
updated = children[1]
|
||||
id = children[2]
|
||||
author = children[3]
|
||||
link = children[4]
|
||||
entry = children[5]
|
||||
|
||||
self.assertEqual(root.tag.split('}')[1], 'feed')
|
||||
self.assertEqual(title.tag.split('}')[1], 'title')
|
||||
self.assertEqual(title.text, 'About This Version')
|
||||
self.assertEqual(updated.tag.split('}')[1], 'updated')
|
||||
self.assertEqual(updated.text, '2011-01-21T11:33:21Z')
|
||||
self.assertEqual(id.tag.split('}')[1], 'id')
|
||||
self.assertEqual(id.text, 'http://localhost/v1.1/')
|
||||
|
||||
self.assertEqual(author.tag.split('}')[1], 'author')
|
||||
author_name = list(author)[0]
|
||||
author_uri = list(author)[1]
|
||||
self.assertEqual(author_name.tag.split('}')[1], 'name')
|
||||
self.assertEqual(author_name.text, 'Rackspace')
|
||||
self.assertEqual(author_uri.tag.split('}')[1], 'uri')
|
||||
self.assertEqual(author_uri.text, 'http://www.rackspace.com/')
|
||||
|
||||
self.assertEqual(link.get('href'),
|
||||
'http://localhost/v1.1/')
|
||||
self.assertEqual(link.get('rel'), 'self')
|
||||
|
||||
self.assertEqual(entry.tag.split('}')[1], 'entry')
|
||||
entry_children = list(entry)
|
||||
entry_id = entry_children[0]
|
||||
entry_title = entry_children[1]
|
||||
entry_updated = entry_children[2]
|
||||
entry_links = (entry_children[3], entry_children[4], entry_children[5])
|
||||
entry_content = entry_children[6]
|
||||
|
||||
self.assertEqual(entry_id.tag.split('}')[1], "id")
|
||||
self.assertEqual(entry_id.text,
|
||||
"http://localhost/v1.1/")
|
||||
self.assertEqual(entry_title.tag.split('}')[1], "title")
|
||||
self.assertEqual(entry_title.get('type'), "text")
|
||||
self.assertEqual(entry_title.text, "Version v1.1")
|
||||
self.assertEqual(entry_updated.tag.split('}')[1], "updated")
|
||||
self.assertEqual(entry_updated.text, "2011-01-21T11:33:21Z")
|
||||
|
||||
for i, link in enumerate(versions_data["version"]["links"]):
|
||||
self.assertEqual(entry_links[i].tag.split('}')[1], "link")
|
||||
for key, val in versions_data["version"]["links"][i].items():
|
||||
self.assertEqual(entry_links[i].get(key), val)
|
||||
|
||||
self.assertEqual(entry_content.tag.split('}')[1], "content")
|
||||
self.assertEqual(entry_content.get('type'), "text")
|
||||
self.assertEqual(entry_content.text,
|
||||
"Version v1.1 CURRENT (2011-01-21T11:33:21Z)")
|
||||
|
||||
@@ -95,31 +95,15 @@ def zone_select(context, specs):
|
||||
class ZonesTest(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ZonesTest, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
fakes.FakeAuthManager.reset_fake_data()
|
||||
fakes.FakeAuthDatabase.data = {}
|
||||
self.flags(allow_admin_api=True)
|
||||
fakes.stub_out_networking(self.stubs)
|
||||
fakes.stub_out_rate_limiting(self.stubs)
|
||||
fakes.stub_out_auth(self.stubs)
|
||||
|
||||
self.allow_admin = FLAGS.allow_admin_api
|
||||
FLAGS.allow_admin_api = True
|
||||
|
||||
self.stubs.Set(nova.db, 'zone_get', zone_get)
|
||||
self.stubs.Set(nova.db, 'zone_update', zone_update)
|
||||
self.stubs.Set(nova.db, 'zone_create', zone_create)
|
||||
self.stubs.Set(nova.db, 'zone_delete', zone_delete)
|
||||
|
||||
self.old_zone_name = FLAGS.zone_name
|
||||
self.old_zone_capabilities = FLAGS.zone_capabilities
|
||||
|
||||
def tearDown(self):
|
||||
self.stubs.UnsetAll()
|
||||
FLAGS.allow_admin_api = self.allow_admin
|
||||
FLAGS.zone_name = self.old_zone_name
|
||||
FLAGS.zone_capabilities = self.old_zone_capabilities
|
||||
super(ZonesTest, self).tearDown()
|
||||
|
||||
def test_get_zone_list_scheduler(self):
|
||||
self.stubs.Set(api, '_call_scheduler', zone_get_all_scheduler)
|
||||
req = webob.Request.blank('/v1.0/zones')
|
||||
@@ -190,8 +174,8 @@ class ZonesTest(test.TestCase):
|
||||
self.assertFalse('username' in res_dict['zone'])
|
||||
|
||||
def test_zone_info(self):
|
||||
FLAGS.zone_name = 'darksecret'
|
||||
FLAGS.zone_capabilities = ['cap1=a;b', 'cap2=c;d']
|
||||
caps = ['cap1=a;b', 'cap2=c;d']
|
||||
self.flags(zone_name='darksecret', zone_capabilities=caps)
|
||||
self.stubs.Set(api, '_call_scheduler', zone_capabilities)
|
||||
|
||||
body = dict(zone=dict(username='zeb', password='sneaky'))
|
||||
@@ -205,7 +189,8 @@ class ZonesTest(test.TestCase):
|
||||
self.assertEqual(res_dict['zone']['cap2'], 'c;d')
|
||||
|
||||
def test_zone_select(self):
|
||||
FLAGS.build_plan_encryption_key = 'c286696d887c9aa0611bbb3e2025a45a'
|
||||
key = 'c286696d887c9aa0611bbb3e2025a45a'
|
||||
self.flags(build_plan_encryption_key=key)
|
||||
self.stubs.Set(api, 'select', zone_select)
|
||||
|
||||
req = webob.Request.blank('/v1.0/zones/select')
|
||||
|
||||
@@ -60,7 +60,10 @@ class FakeGlance(object):
|
||||
'container_format': 'ovf'},
|
||||
'image_data': StringIO.StringIO('')}}
|
||||
|
||||
def __init__(self, host, port=None, use_ssl=False):
|
||||
def __init__(self, host, port=None, use_ssl=False, auth_tok=None):
|
||||
pass
|
||||
|
||||
def set_auth_token(self, auth_tok):
|
||||
pass
|
||||
|
||||
def get_image_meta(self, image_id):
|
||||
|
||||
@@ -23,7 +23,6 @@ from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova.auth import manager
|
||||
from nova.virt import hyperv
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -34,11 +33,9 @@ class HyperVTestCase(test.TestCase):
|
||||
"""Test cases for the Hyper-V driver"""
|
||||
def setUp(self):
|
||||
super(HyperVTestCase, self).setUp()
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext(self.user, self.project)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
|
||||
def test_create_destroy(self):
|
||||
"""Create a VM and destroy it"""
|
||||
|
||||
@@ -31,6 +31,9 @@ class StubGlanceClient(object):
|
||||
self.add_response = add_response
|
||||
self.update_response = update_response
|
||||
|
||||
def set_auth_token(self, auth_tok):
|
||||
pass
|
||||
|
||||
def get_image_meta(self, image_id):
|
||||
return self.images[image_id]
|
||||
|
||||
|
||||
@@ -305,5 +305,6 @@ class ServersTest(integrated_helpers._IntegratedTestBase):
|
||||
# Cleanup
|
||||
self._delete_server(server_id)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -23,7 +23,6 @@ import datetime
|
||||
import mox
|
||||
import novaclient.exceptions
|
||||
import stubout
|
||||
import webob
|
||||
|
||||
from mox import IgnoreArg
|
||||
from nova import context
|
||||
@@ -34,12 +33,10 @@ from nova import service
|
||||
from nova import test
|
||||
from nova import rpc
|
||||
from nova import utils
|
||||
from nova.auth import manager as auth_manager
|
||||
from nova.scheduler import api
|
||||
from nova.scheduler import manager
|
||||
from nova.scheduler import driver
|
||||
from nova.compute import power_state
|
||||
from nova.db.sqlalchemy import models
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -250,23 +247,17 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
volume_driver='nova.volume.driver.FakeISCSIDriver',
|
||||
scheduler_driver='nova.scheduler.simple.SimpleScheduler')
|
||||
self.scheduler = manager.SchedulerManager()
|
||||
self.manager = auth_manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_user(self.user)
|
||||
self.manager.delete_project(self.project)
|
||||
super(SimpleDriverTestCase, self).tearDown()
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
|
||||
def _create_instance(self, **kwargs):
|
||||
"""Create a test instance"""
|
||||
inst = {}
|
||||
inst['image_id'] = 1
|
||||
inst['reservation_id'] = 'r-fakeres'
|
||||
inst['user_id'] = self.user.id
|
||||
inst['project_id'] = self.project.id
|
||||
inst['user_id'] = self.user_id
|
||||
inst['project_id'] = self.project_id
|
||||
inst['instance_type_id'] = '1'
|
||||
inst['vcpus'] = kwargs.get('vcpus', 1)
|
||||
inst['ami_launch_index'] = 0
|
||||
@@ -485,11 +476,6 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
self.assertEqual(host, 'host2')
|
||||
volume1.delete_volume(self.context, volume_id1)
|
||||
db.volume_destroy(self.context, volume_id2)
|
||||
dic = {'service_id': s_ref['id'],
|
||||
'vcpus': 16, 'memory_mb': 32, 'local_gb': 100,
|
||||
'vcpus_used': 16, 'memory_mb_used': 12, 'local_gb_used': 10,
|
||||
'hypervisor_type': 'qemu', 'hypervisor_version': 12003,
|
||||
'cpu_info': ''}
|
||||
|
||||
def test_doesnt_report_disabled_hosts_as_up(self):
|
||||
"""Ensures driver doesn't find hosts before they are enabled"""
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
Tests For Zone Aware Scheduler.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import nova.db
|
||||
|
||||
from nova import exception
|
||||
@@ -327,3 +329,19 @@ class ZoneAwareSchedulerTestCase(test.TestCase):
|
||||
sched._provision_resource_from_blob(None, request_spec, 1,
|
||||
request_spec, {})
|
||||
self.assertTrue(was_called)
|
||||
|
||||
def test_decrypt_blob(self):
|
||||
"""Test that the decrypt method works."""
|
||||
|
||||
fixture = FakeZoneAwareScheduler()
|
||||
test_data = {"foo": "bar"}
|
||||
|
||||
class StubDecryptor(object):
|
||||
def decryptor(self, key):
|
||||
return lambda blob: blob
|
||||
|
||||
self.stubs.Set(zone_aware_scheduler, 'crypto',
|
||||
StubDecryptor())
|
||||
|
||||
self.assertEqual(fixture._decrypt_blob(test_data),
|
||||
json.dumps(test_data))
|
||||
|
||||
+10
-11
@@ -16,7 +16,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import unittest
|
||||
import webob
|
||||
|
||||
from nova import context
|
||||
@@ -41,7 +40,7 @@ class FakeApiRequest(object):
|
||||
class AccessTestCase(test.TestCase):
|
||||
def _env_for(self, ctxt, action):
|
||||
env = {}
|
||||
env['ec2.context'] = ctxt
|
||||
env['nova.context'] = ctxt
|
||||
env['ec2.request'] = FakeApiRequest(action)
|
||||
return env
|
||||
|
||||
@@ -93,7 +92,11 @@ class AccessTestCase(test.TestCase):
|
||||
super(AccessTestCase, self).tearDown()
|
||||
|
||||
def response_status(self, user, methodName):
|
||||
ctxt = context.RequestContext(user, self.project)
|
||||
roles = manager.AuthManager().get_active_roles(user, self.project)
|
||||
ctxt = context.RequestContext(user.id,
|
||||
self.project.id,
|
||||
is_admin=user.is_admin(),
|
||||
roles=roles)
|
||||
environ = self._env_for(ctxt, methodName)
|
||||
req = webob.Request.blank('/', environ)
|
||||
resp = req.get_response(self.mw)
|
||||
@@ -105,30 +108,26 @@ class AccessTestCase(test.TestCase):
|
||||
def shouldDeny(self, user, methodName):
|
||||
self.assertEqual(401, self.response_status(user, methodName))
|
||||
|
||||
def test_001_allow_all(self):
|
||||
def test_allow_all(self):
|
||||
users = [self.testadmin, self.testpmsys, self.testnet, self.testsys]
|
||||
for user in users:
|
||||
self.shouldAllow(user, '_allow_all')
|
||||
|
||||
def test_002_allow_none(self):
|
||||
def test_allow_none(self):
|
||||
self.shouldAllow(self.testadmin, '_allow_none')
|
||||
users = [self.testpmsys, self.testnet, self.testsys]
|
||||
for user in users:
|
||||
self.shouldDeny(user, '_allow_none')
|
||||
|
||||
def test_003_allow_project_manager(self):
|
||||
def test_allow_project_manager(self):
|
||||
for user in [self.testadmin, self.testpmsys]:
|
||||
self.shouldAllow(user, '_allow_project_manager')
|
||||
for user in [self.testnet, self.testsys]:
|
||||
self.shouldDeny(user, '_allow_project_manager')
|
||||
|
||||
def test_004_allow_sys_and_net(self):
|
||||
def test_allow_sys_and_net(self):
|
||||
for user in [self.testadmin, self.testnet, self.testsys]:
|
||||
self.shouldAllow(user, '_allow_sys_and_net')
|
||||
# denied because it doesn't have the per project sysadmin
|
||||
for user in [self.testpmsys]:
|
||||
self.shouldDeny(user, '_allow_sys_and_net')
|
||||
|
||||
if __name__ == "__main__":
|
||||
# TODO: Implement use_fake as an option
|
||||
unittest.main()
|
||||
|
||||
@@ -25,7 +25,6 @@ from nova import log as logging
|
||||
from nova import rpc
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.api.ec2 import admin
|
||||
from nova.image import fake
|
||||
|
||||
@@ -39,7 +38,7 @@ class AdminApiTestCase(test.TestCase):
|
||||
super(AdminApiTestCase, self).setUp()
|
||||
self.flags(connection_type='fake')
|
||||
|
||||
self.conn = rpc.Connection.instance()
|
||||
self.conn = rpc.create_connection()
|
||||
|
||||
# set up our cloud
|
||||
self.api = admin.AdminController()
|
||||
@@ -51,11 +50,11 @@ class AdminApiTestCase(test.TestCase):
|
||||
self.volume = self.start_service('volume')
|
||||
self.image_service = utils.import_object(FLAGS.image_service)
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
|
||||
self.project = self.manager.create_project('proj', 'admin', 'proj')
|
||||
self.context = context.RequestContext(user=self.user,
|
||||
project=self.project)
|
||||
self.user_id = 'admin'
|
||||
self.project_id = 'admin'
|
||||
self.context = context.RequestContext(self.user_id,
|
||||
self.project_id,
|
||||
True)
|
||||
|
||||
def fake_show(meh, context, id):
|
||||
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
|
||||
@@ -73,11 +72,6 @@ class AdminApiTestCase(test.TestCase):
|
||||
|
||||
self.stubs.Set(rpc, 'cast', finish_cast)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(AdminApiTestCase, self).tearDown()
|
||||
|
||||
def test_block_external_ips(self):
|
||||
"""Make sure provider firewall rules are created."""
|
||||
result = self.api.block_external_addresses(self.context, '1.1.1.1/32')
|
||||
|
||||
+8
-68
@@ -30,11 +30,11 @@ import webob
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova import wsgi
|
||||
from nova.api import ec2
|
||||
from nova.api.ec2 import apirequest
|
||||
from nova.api.ec2 import cloud
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova.auth import manager
|
||||
|
||||
|
||||
class FakeHttplibSocket(object):
|
||||
@@ -192,10 +192,13 @@ class ApiEc2TestCase(test.TestCase):
|
||||
"""Unit test for the cloud controller on an EC2 API"""
|
||||
def setUp(self):
|
||||
super(ApiEc2TestCase, self).setUp()
|
||||
self.manager = manager.AuthManager()
|
||||
self.host = '127.0.0.1'
|
||||
self.app = ec2.Authenticate(ec2.Requestify(ec2.Executor(),
|
||||
'nova.api.ec2.cloud.CloudController'))
|
||||
# NOTE(vish): skipping the Authorizer
|
||||
roles = ['sysadmin', 'netadmin']
|
||||
ctxt = context.RequestContext('fake', 'fake', roles=roles)
|
||||
self.app = wsgi.InjectContext(ctxt,
|
||||
ec2.Requestify(ec2.Authorizer(ec2.Executor()),
|
||||
'nova.api.ec2.cloud.CloudController'))
|
||||
|
||||
def expect_http(self, host=None, is_secure=False, api_version=None):
|
||||
"""Returns a new EC2 connection"""
|
||||
@@ -246,39 +249,25 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.expect_http(api_version='2010-10-30')
|
||||
self.mox.ReplayAll()
|
||||
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
# Any request should be fine
|
||||
self.ec2.get_all_instances()
|
||||
self.assertTrue(self.ec2.APIVersion in self.http.getresponsebody(),
|
||||
'The version in the xmlns of the response does '
|
||||
'not match the API version given in the request.')
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_describe_instances(self):
|
||||
"""Test that, after creating a user and a project, the describe
|
||||
instances call to the API works properly"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.assertEqual(self.ec2.get_all_instances(), [])
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_terminate_invalid_instance(self):
|
||||
"""Attempt to terminate an invalid instance"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.assertRaises(EC2ResponseError, self.ec2.terminate_instances,
|
||||
"i-00000005")
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_get_all_key_pairs(self):
|
||||
"""Test that, after creating a user and project and generating
|
||||
@@ -287,16 +276,12 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \
|
||||
for x in range(random.randint(4, 8)))
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
# NOTE(vish): create depends on pool, so call helper directly
|
||||
cloud._gen_key(context.get_admin_context(), user.id, keyname)
|
||||
cloud._gen_key(context.get_admin_context(), 'fake', keyname)
|
||||
|
||||
rv = self.ec2.get_all_key_pairs()
|
||||
results = [k for k in rv if k.name == keyname]
|
||||
self.assertEquals(len(results), 1)
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_create_duplicate_key_pair(self):
|
||||
"""Test that, after successfully generating a keypair,
|
||||
@@ -305,8 +290,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
keyname = "".join(random.choice("sdiuisudfsdcnpaqwertasd") \
|
||||
for x in range(random.randint(4, 8)))
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
# NOTE(vish): create depends on pool, so call helper directly
|
||||
self.ec2.create_key_pair('test')
|
||||
|
||||
@@ -325,27 +308,16 @@ class ApiEc2TestCase(test.TestCase):
|
||||
"""Test that we can retrieve security groups"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
rv = self.ec2.get_all_security_groups()
|
||||
|
||||
self.assertEquals(len(rv), 1)
|
||||
self.assertEquals(rv[0].name, 'default')
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_create_delete_security_group(self):
|
||||
"""Test that we can create a security group"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
# At the moment, you need both of these to actually be netadmin
|
||||
self.manager.add_role('fake', 'netadmin')
|
||||
project.add_role('fake', 'netadmin')
|
||||
|
||||
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
|
||||
for x in range(random.randint(4, 8)))
|
||||
@@ -364,9 +336,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
|
||||
self.ec2.delete_security_group(security_group_name)
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
def test_authorize_revoke_security_group_cidr(self):
|
||||
"""
|
||||
Test that we can add and remove CIDR based rules
|
||||
@@ -374,12 +343,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
# At the moment, you need both of these to actually be netadmin
|
||||
self.manager.add_role('fake', 'netadmin')
|
||||
project.add_role('fake', 'netadmin')
|
||||
|
||||
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
|
||||
for x in range(random.randint(4, 8)))
|
||||
@@ -426,9 +389,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.assertEqual(len(rv), 1)
|
||||
self.assertEqual(rv[0].name, 'default')
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
return
|
||||
|
||||
def test_authorize_revoke_security_group_cidr_v6(self):
|
||||
@@ -438,12 +398,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
# At the moment, you need both of these to actually be netadmin
|
||||
self.manager.add_role('fake', 'netadmin')
|
||||
project.add_role('fake', 'netadmin')
|
||||
|
||||
security_group_name = "".join(random.choice("sdiuisudfsdcnpaqwertasd")
|
||||
for x in range(random.randint(4, 8)))
|
||||
@@ -489,9 +443,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.assertEqual(len(rv), 1)
|
||||
self.assertEqual(rv[0].name, 'default')
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
return
|
||||
|
||||
def test_authorize_revoke_security_group_foreign_group(self):
|
||||
@@ -501,12 +452,6 @@ class ApiEc2TestCase(test.TestCase):
|
||||
"""
|
||||
self.expect_http()
|
||||
self.mox.ReplayAll()
|
||||
user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
|
||||
project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
|
||||
# At the moment, you need both of these to actually be netadmin
|
||||
self.manager.add_role('fake', 'netadmin')
|
||||
project.add_role('fake', 'netadmin')
|
||||
|
||||
rand_string = 'sdiuisudfsdcnpaqwertasd'
|
||||
security_group_name = "".join(random.choice(rand_string)
|
||||
@@ -560,8 +505,3 @@ class ApiEc2TestCase(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
self.ec2.delete_security_group(security_group_name)
|
||||
|
||||
self.manager.delete_project(project)
|
||||
self.manager.delete_user(user)
|
||||
|
||||
return
|
||||
|
||||
+9
-10
@@ -102,7 +102,7 @@ class _AuthManagerBaseTestCase(test.TestCase):
|
||||
self.assertEqual('classified', u.secret)
|
||||
self.assertEqual('private-party', u.access)
|
||||
|
||||
def test_004_signature_is_valid(self):
|
||||
def test_signature_is_valid(self):
|
||||
with user_generator(self.manager, name='admin', secret='admin',
|
||||
access='admin'):
|
||||
with project_generator(self.manager, name="admin",
|
||||
@@ -141,15 +141,14 @@ class _AuthManagerBaseTestCase(test.TestCase):
|
||||
'127.0.0.1',
|
||||
'/services/Cloud'))
|
||||
|
||||
def test_005_can_get_credentials(self):
|
||||
return
|
||||
credentials = self.manager.get_user('test1').get_credentials()
|
||||
self.assertEqual(credentials,
|
||||
'export EC2_ACCESS_KEY="access"\n' +
|
||||
'export EC2_SECRET_KEY="secret"\n' +
|
||||
'export EC2_URL="http://127.0.0.1:8773/services/Cloud"\n' +
|
||||
'export S3_URL="http://127.0.0.1:3333/"\n' +
|
||||
'export EC2_USER_ID="test1"\n')
|
||||
def test_can_get_credentials(self):
|
||||
st = {'access': 'access', 'secret': 'secret'}
|
||||
with user_and_project_generator(self.manager, user_state=st) as (u, p):
|
||||
credentials = self.manager.get_environment_rc(u, p)
|
||||
LOG.debug(credentials)
|
||||
self.assertTrue('export EC2_ACCESS_KEY="access:testproj"\n'
|
||||
in credentials)
|
||||
self.assertTrue('export EC2_SECRET_KEY="secret"\n' in credentials)
|
||||
|
||||
def test_can_list_users(self):
|
||||
with user_generator(self.manager):
|
||||
|
||||
+15
-41
@@ -34,7 +34,6 @@ from nova import network
|
||||
from nova import rpc
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.api.ec2 import cloud
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova.image import fake
|
||||
@@ -50,7 +49,7 @@ class CloudTestCase(test.TestCase):
|
||||
self.flags(connection_type='fake',
|
||||
stub_network=True)
|
||||
|
||||
self.conn = rpc.Connection.instance()
|
||||
self.conn = rpc.create_connection()
|
||||
|
||||
# set up our cloud
|
||||
self.cloud = cloud.CloudController()
|
||||
@@ -62,12 +61,11 @@ class CloudTestCase(test.TestCase):
|
||||
self.volume = self.start_service('volume')
|
||||
self.image_service = utils.import_object(FLAGS.image_service)
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
|
||||
self.project = self.manager.create_project('proj', 'admin', 'proj')
|
||||
self.context = context.RequestContext(user=self.user,
|
||||
project=self.project)
|
||||
host = self.network.host
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id,
|
||||
self.project_id,
|
||||
True)
|
||||
|
||||
def fake_show(meh, context, id):
|
||||
return {'id': 1, 'container_format': 'ami',
|
||||
@@ -87,17 +85,15 @@ class CloudTestCase(test.TestCase):
|
||||
self.stubs.Set(rpc, 'cast', finish_cast)
|
||||
|
||||
def tearDown(self):
|
||||
networks = db.project_get_networks(self.context, self.project.id,
|
||||
networks = db.project_get_networks(self.context, self.project_id,
|
||||
associate=False)
|
||||
for network in networks:
|
||||
db.network_disassociate(self.context, network['id'])
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(CloudTestCase, self).tearDown()
|
||||
|
||||
def _create_key(self, name):
|
||||
# NOTE(vish): create depends on pool, so just call helper directly
|
||||
return cloud._gen_key(self.context, self.context.user.id, name)
|
||||
return cloud._gen_key(self.context, self.context.user_id, name)
|
||||
|
||||
def test_describe_regions(self):
|
||||
"""Makes sure describe regions runs without raising an exception"""
|
||||
@@ -326,22 +322,15 @@ class CloudTestCase(test.TestCase):
|
||||
revoke = self.cloud.revoke_security_group_ingress
|
||||
self.assertTrue(revoke(self.context, group_name=sec['name'], **kwargs))
|
||||
|
||||
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):
|
||||
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'})
|
||||
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))
|
||||
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_missing_protocol_params(self):
|
||||
sec = db.security_group_create(self.context,
|
||||
@@ -961,21 +950,6 @@ 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',
|
||||
@@ -1004,7 +978,7 @@ class CloudTestCase(test.TestCase):
|
||||
key = RSA.load_key_string(private_key, callback=lambda: None)
|
||||
bio = BIO.MemoryBuffer()
|
||||
public_key = db.key_pair_get(self.context,
|
||||
self.context.user.id,
|
||||
self.context.user_id,
|
||||
'test')['public_key']
|
||||
key.save_pub_key_bio(bio)
|
||||
converted = crypto.ssl_pub_to_ssh_pub(bio.read())
|
||||
@@ -1028,7 +1002,7 @@ class CloudTestCase(test.TestCase):
|
||||
'mytestfprint')
|
||||
self.assertTrue(result1)
|
||||
keydata = db.key_pair_get(self.context,
|
||||
self.context.user.id,
|
||||
self.context.user_id,
|
||||
'testimportkey1')
|
||||
self.assertEqual('mytestpubkey', keydata['public_key'])
|
||||
self.assertEqual('mytestfprint', keydata['fingerprint'])
|
||||
@@ -1045,7 +1019,7 @@ class CloudTestCase(test.TestCase):
|
||||
dummypub)
|
||||
self.assertTrue(result2)
|
||||
keydata = db.key_pair_get(self.context,
|
||||
self.context.user.id,
|
||||
self.context.user_id,
|
||||
'testimportkey2')
|
||||
self.assertEqual(dummypub, keydata['public_key'])
|
||||
self.assertEqual(dummyfprint, keydata['fingerprint'])
|
||||
|
||||
+20
-31
@@ -19,10 +19,6 @@
|
||||
Tests For Compute
|
||||
"""
|
||||
|
||||
import mox
|
||||
import stubout
|
||||
|
||||
from nova.auth import manager
|
||||
from nova import compute
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import manager as compute_manager
|
||||
@@ -67,10 +63,9 @@ class ComputeTestCase(test.TestCase):
|
||||
network_manager='nova.network.manager.FlatManager')
|
||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||
self.compute_api = compute.API()
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
test_notifier.NOTIFICATIONS = []
|
||||
|
||||
def fake_show(meh, context, id):
|
||||
@@ -78,19 +73,14 @@ class ComputeTestCase(test.TestCase):
|
||||
|
||||
self.stubs.Set(nova.image.fake._FakeImageService, 'show', fake_show)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_user(self.user)
|
||||
self.manager.delete_project(self.project)
|
||||
super(ComputeTestCase, self).tearDown()
|
||||
|
||||
def _create_instance(self, params={}):
|
||||
"""Create a test instance"""
|
||||
inst = {}
|
||||
inst['image_ref'] = 1
|
||||
inst['reservation_id'] = 'r-fakeres'
|
||||
inst['launch_time'] = '10'
|
||||
inst['user_id'] = self.user.id
|
||||
inst['project_id'] = self.project.id
|
||||
inst['user_id'] = self.user_id
|
||||
inst['project_id'] = self.project_id
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
inst['instance_type_id'] = type_id
|
||||
inst['ami_launch_index'] = 0
|
||||
@@ -115,8 +105,8 @@ class ComputeTestCase(test.TestCase):
|
||||
def _create_group(self):
|
||||
values = {'name': 'testgroup',
|
||||
'description': 'testgroup',
|
||||
'user_id': self.user.id,
|
||||
'project_id': self.project.id}
|
||||
'user_id': self.user_id,
|
||||
'project_id': self.project_id}
|
||||
return db.security_group_create(self.context, values)
|
||||
|
||||
def _get_dummy_instance(self):
|
||||
@@ -350,8 +340,8 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.create')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['tenant_id'], self.project_id)
|
||||
self.assertEquals(payload['user_id'], self.user_id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
@@ -374,8 +364,8 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.delete')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['tenant_id'], self.project_id)
|
||||
self.assertEquals(payload['user_id'], self.user_id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
@@ -420,7 +410,7 @@ class ComputeTestCase(test.TestCase):
|
||||
def fake(*args, **kwargs):
|
||||
pass
|
||||
|
||||
self.stubs.Set(self.compute.driver, 'finish_resize', fake)
|
||||
self.stubs.Set(self.compute.driver, 'finish_migration', fake)
|
||||
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
|
||||
context = self.context.elevated()
|
||||
instance_id = self._create_instance()
|
||||
@@ -457,8 +447,8 @@ class ComputeTestCase(test.TestCase):
|
||||
self.assertEquals(msg['priority'], 'INFO')
|
||||
self.assertEquals(msg['event_type'], 'compute.instance.resize.prep')
|
||||
payload = msg['payload']
|
||||
self.assertEquals(payload['tenant_id'], self.project.id)
|
||||
self.assertEquals(payload['user_id'], self.user.id)
|
||||
self.assertEquals(payload['tenant_id'], self.project_id)
|
||||
self.assertEquals(payload['user_id'], self.user_id)
|
||||
self.assertEquals(payload['instance_id'], instance_id)
|
||||
self.assertEquals(payload['instance_type'], 'm1.tiny')
|
||||
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
@@ -506,8 +496,8 @@ class ComputeTestCase(test.TestCase):
|
||||
db.instance_update(self.context, instance_id,
|
||||
{'instance_type_id': inst_type['id']})
|
||||
|
||||
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
||||
context, instance_id, 1)
|
||||
self.assertRaises(exception.CannotResizeToSmallerSize,
|
||||
self.compute_api.resize, context, instance_id, 1)
|
||||
|
||||
self.compute.terminate_instance(context, instance_id)
|
||||
|
||||
@@ -518,8 +508,8 @@ class ComputeTestCase(test.TestCase):
|
||||
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
|
||||
self.assertRaises(exception.ApiError, self.compute_api.resize,
|
||||
context, instance_id, 1)
|
||||
self.assertRaises(exception.CannotResizeToSameSize,
|
||||
self.compute_api.resize, context, instance_id, 1)
|
||||
|
||||
self.compute.terminate_instance(context, instance_id)
|
||||
|
||||
@@ -531,8 +521,8 @@ class ComputeTestCase(test.TestCase):
|
||||
def fake(*args, **kwargs):
|
||||
pass
|
||||
|
||||
self.stubs.Set(self.compute.driver, 'finish_resize', fake)
|
||||
self.stubs.Set(self.compute.driver, 'revert_resize', fake)
|
||||
self.stubs.Set(self.compute.driver, 'finish_migration', fake)
|
||||
self.stubs.Set(self.compute.driver, 'revert_migration', fake)
|
||||
self.stubs.Set(self.compute.network_api, 'get_instance_nw_info', fake)
|
||||
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
@@ -849,7 +839,6 @@ class ComputeTestCase(test.TestCase):
|
||||
|
||||
def test_run_kill_vm(self):
|
||||
"""Detect when a vm is terminated behind the scenes"""
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.stubs.Set(compute_manager.ComputeManager,
|
||||
'_report_driver_status', nop_report_driver_status)
|
||||
|
||||
|
||||
@@ -26,10 +26,9 @@ from nova import exception
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.console import manager as console_manager
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('console_driver', 'nova.console.manager')
|
||||
|
||||
|
||||
class ConsoleTestCase(test.TestCase):
|
||||
@@ -39,17 +38,11 @@ class ConsoleTestCase(test.TestCase):
|
||||
self.flags(console_driver='nova.console.fake.FakeConsoleProxy',
|
||||
stub_compute=True)
|
||||
self.console = utils.import_object(FLAGS.console_manager)
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake')
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.get_admin_context()
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.host = 'test_compute_host'
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_user(self.user)
|
||||
self.manager.delete_project(self.project)
|
||||
super(ConsoleTestCase, self).tearDown()
|
||||
|
||||
def _create_instance(self):
|
||||
"""Create a test instance"""
|
||||
inst = {}
|
||||
@@ -58,8 +51,8 @@ class ConsoleTestCase(test.TestCase):
|
||||
inst['image_id'] = 1
|
||||
inst['reservation_id'] = 'r-fakeres'
|
||||
inst['launch_time'] = '10'
|
||||
inst['user_id'] = self.user.id
|
||||
inst['project_id'] = self.project.id
|
||||
inst['user_id'] = self.user_id
|
||||
inst['project_id'] = self.project_id
|
||||
inst['instance_type_id'] = 1
|
||||
inst['ami_launch_index'] = 0
|
||||
return db.instance_create(self.context, inst)['id']
|
||||
|
||||
+13
-21
@@ -22,7 +22,6 @@ from nova import test
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova.auth import manager
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
@@ -45,42 +44,35 @@ def _setup_networking(instance_id, ip='1.2.3.4', flo_addr='1.2.1.2'):
|
||||
db.fixed_ip_create(ctxt, fixed_ip)
|
||||
fix_ref = db.fixed_ip_get_by_address(ctxt, ip)
|
||||
db.floating_ip_create(ctxt, {'address': flo_addr,
|
||||
'fixed_ip_id': fix_ref.id})
|
||||
'fixed_ip_id': fix_ref['id']})
|
||||
|
||||
|
||||
class DbApiTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(DbApiTestCase, self).setUp()
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
|
||||
self.project = self.manager.create_project('proj', 'admin', 'proj')
|
||||
self.context = context.RequestContext(user=self.user,
|
||||
project=self.project)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(DbApiTestCase, self).tearDown()
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
|
||||
def test_instance_get_project_vpn(self):
|
||||
result = db.fixed_ip_get_all(self.context)
|
||||
values = {'instance_type_id': FLAGS.default_instance_type,
|
||||
'image_ref': FLAGS.vpn_image_id,
|
||||
'project_id': self.project.id
|
||||
'project_id': self.project_id,
|
||||
}
|
||||
instance = db.instance_create(self.context, values)
|
||||
result = db.instance_get_project_vpn(self.context, self.project.id)
|
||||
self.assertEqual(instance.id, result.id)
|
||||
result = db.instance_get_project_vpn(self.context.elevated(),
|
||||
self.project_id)
|
||||
self.assertEqual(instance['id'], result['id'])
|
||||
|
||||
def test_instance_get_project_vpn_joins(self):
|
||||
result = db.fixed_ip_get_all(self.context)
|
||||
values = {'instance_type_id': FLAGS.default_instance_type,
|
||||
'image_ref': FLAGS.vpn_image_id,
|
||||
'project_id': self.project.id
|
||||
'project_id': self.project_id,
|
||||
}
|
||||
instance = db.instance_create(self.context, values)
|
||||
_setup_networking(instance.id)
|
||||
result = db.instance_get_project_vpn(self.context, self.project.id)
|
||||
self.assertEqual(instance.id, result.id)
|
||||
_setup_networking(instance['id'])
|
||||
result = db.instance_get_project_vpn(self.context.elevated(),
|
||||
self.project_id)
|
||||
self.assertEqual(instance['id'], result['id'])
|
||||
self.assertEqual(result['fixed_ips'][0]['floating_ips'][0].address,
|
||||
'1.2.1.2')
|
||||
|
||||
+17
-54
@@ -32,7 +32,6 @@ from nova import flags
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.api.ec2 import cloud
|
||||
from nova.auth import manager
|
||||
from nova.compute import power_state
|
||||
from nova.virt.libvirt import connection
|
||||
from nova.virt.libvirt import firewall
|
||||
@@ -154,36 +153,15 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
super(LibvirtConnTestCase, self).setUp()
|
||||
connection._late_load_cheetah()
|
||||
self.flags(fake_call=True)
|
||||
self.manager = manager.AuthManager()
|
||||
|
||||
try:
|
||||
pjs = self.manager.get_projects()
|
||||
pjs = [p for p in pjs if p.name == 'fake']
|
||||
if 0 != len(pjs):
|
||||
self.manager.delete_project(pjs[0])
|
||||
|
||||
users = self.manager.get_users()
|
||||
users = [u for u in users if u.name == 'fake']
|
||||
if 0 != len(users):
|
||||
self.manager.delete_user(users[0])
|
||||
except Exception, e:
|
||||
pass
|
||||
|
||||
users = self.manager.get_users()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
self.context = context.get_admin_context()
|
||||
FLAGS.instances_path = ''
|
||||
self.call_libvirt_dependant_setup = False
|
||||
self.test_ip = '10.11.12.13'
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(LibvirtConnTestCase, self).tearDown()
|
||||
|
||||
test_instance = {'memory_kb': '1024000',
|
||||
'basepath': '/some/path',
|
||||
'bridge_name': 'br100',
|
||||
@@ -239,7 +217,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
'mac_address': 'fake',
|
||||
'ip_address': 'fake',
|
||||
'dhcp_server': 'fake',
|
||||
'extra_params': 'fake'
|
||||
'extra_params': 'fake',
|
||||
}
|
||||
|
||||
# Creating mocks
|
||||
@@ -368,7 +346,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
conn = connection.LibvirtConnection(False)
|
||||
conn.snapshot(instance_ref, recv_meta['id'])
|
||||
conn.snapshot(self.context, instance_ref, recv_meta['id'])
|
||||
|
||||
snapshot = image_service.show(context, recv_meta['id'])
|
||||
self.assertEquals(snapshot['properties']['image_state'], 'available')
|
||||
@@ -408,7 +386,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
conn = connection.LibvirtConnection(False)
|
||||
conn.snapshot(instance_ref, recv_meta['id'])
|
||||
conn.snapshot(self.context, instance_ref, recv_meta['id'])
|
||||
|
||||
snapshot = image_service.show(context, recv_meta['id'])
|
||||
self.assertEquals(snapshot['properties']['image_state'], 'available')
|
||||
@@ -441,8 +419,8 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
self.assertEquals(parameters[1].get('value'), 'fake')
|
||||
|
||||
def _check_xml_and_container(self, instance):
|
||||
user_context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
user_context = context.RequestContext(self.user_id,
|
||||
self.project_id)
|
||||
instance_ref = db.instance_create(user_context, instance)
|
||||
_setup_networking(instance_ref['id'], self.test_ip)
|
||||
|
||||
@@ -470,11 +448,10 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
|
||||
def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
|
||||
rescue=False):
|
||||
user_context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
user_context = context.RequestContext(self.user_id, self.project_id)
|
||||
instance_ref = db.instance_create(user_context, instance)
|
||||
network_ref = db.project_get_networks(context.get_admin_context(),
|
||||
self.project.id)[0]
|
||||
self.project_id)[0]
|
||||
|
||||
_setup_networking(instance_ref['id'], self.test_ip)
|
||||
|
||||
@@ -759,7 +736,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
network_info = _create_network_info()
|
||||
|
||||
try:
|
||||
conn.spawn(instance, network_info)
|
||||
conn.spawn(self.context, instance, network_info)
|
||||
except Exception, e:
|
||||
count = (0 <= str(e.message).find('Unexpected method call'))
|
||||
|
||||
@@ -802,11 +779,9 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(IptablesFirewallTestCase, self).setUp()
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
|
||||
class FakeLibvirtConnection(object):
|
||||
@@ -832,11 +807,6 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
connection.libxml2 = __import__('libxml2')
|
||||
return True
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(IptablesFirewallTestCase, self).tearDown()
|
||||
|
||||
in_nat_rules = [
|
||||
'# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011',
|
||||
'*nat',
|
||||
@@ -1119,11 +1089,9 @@ class NWFilterTestCase(test.TestCase):
|
||||
class Mock(object):
|
||||
pass
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext(self.user, self.project)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
|
||||
self.fake_libvirt_connection = Mock()
|
||||
|
||||
@@ -1131,11 +1099,6 @@ class NWFilterTestCase(test.TestCase):
|
||||
self.fw = firewall.NWFilterFirewall(
|
||||
lambda: self.fake_libvirt_connection)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
super(NWFilterTestCase, self).tearDown()
|
||||
|
||||
def test_cidr_rule_nwfilter_xml(self):
|
||||
cloud_controller = cloud.CloudController()
|
||||
cloud_controller.create_security_group(self.context,
|
||||
|
||||
@@ -21,8 +21,6 @@ Unittets for S3 objectstore clone.
|
||||
"""
|
||||
|
||||
import boto
|
||||
import glob
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -30,12 +28,9 @@ import tempfile
|
||||
from boto import exception as boto_exception
|
||||
from boto.s3 import connection as s3
|
||||
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import wsgi
|
||||
from nova import test
|
||||
from nova.auth import manager
|
||||
from nova.objectstore import s3server
|
||||
|
||||
|
||||
@@ -57,15 +52,9 @@ class S3APITestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
"""Setup users, projects, and start a test server."""
|
||||
super(S3APITestCase, self).setUp()
|
||||
self.flags(auth_driver='nova.auth.ldapdriver.FakeLdapDriver',
|
||||
buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'),
|
||||
self.flags(buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'),
|
||||
s3_host='127.0.0.1')
|
||||
|
||||
self.auth_manager = manager.AuthManager()
|
||||
self.admin_user = self.auth_manager.create_user('admin', admin=True)
|
||||
self.admin_project = self.auth_manager.create_project('admin',
|
||||
self.admin_user)
|
||||
|
||||
shutil.rmtree(FLAGS.buckets_path)
|
||||
os.mkdir(FLAGS.buckets_path)
|
||||
|
||||
@@ -80,8 +69,8 @@ class S3APITestCase(test.TestCase):
|
||||
boto.config.add_section('Boto')
|
||||
|
||||
boto.config.set('Boto', 'num_retries', '0')
|
||||
conn = s3.S3Connection(aws_access_key_id=self.admin_user.access,
|
||||
aws_secret_access_key=self.admin_user.secret,
|
||||
conn = s3.S3Connection(aws_access_key_id='fake',
|
||||
aws_secret_access_key='fake',
|
||||
host=FLAGS.s3_host,
|
||||
port=FLAGS.s3_port,
|
||||
is_secure=False,
|
||||
@@ -104,11 +93,11 @@ class S3APITestCase(test.TestCase):
|
||||
self.assertEquals(buckets[0].name, name, "Wrong name")
|
||||
return True
|
||||
|
||||
def test_000_list_buckets(self):
|
||||
def test_list_buckets(self):
|
||||
"""Make sure we are starting with no buckets."""
|
||||
self._ensure_no_buckets(self.conn.get_all_buckets())
|
||||
|
||||
def test_001_create_and_delete_bucket(self):
|
||||
def test_create_and_delete_bucket(self):
|
||||
"""Test bucket creation and deletion."""
|
||||
bucket_name = 'testbucket'
|
||||
|
||||
@@ -117,7 +106,7 @@ class S3APITestCase(test.TestCase):
|
||||
self.conn.delete_bucket(bucket_name)
|
||||
self._ensure_no_buckets(self.conn.get_all_buckets())
|
||||
|
||||
def test_002_create_bucket_and_key_and_delete_key_again(self):
|
||||
def test_create_bucket_and_key_and_delete_key_again(self):
|
||||
"""Test key operations on buckets."""
|
||||
bucket_name = 'testbucket'
|
||||
key_name = 'somekey'
|
||||
@@ -146,8 +135,6 @@ class S3APITestCase(test.TestCase):
|
||||
bucket_name)
|
||||
|
||||
def tearDown(self):
|
||||
"""Tear down auth and test server."""
|
||||
self.auth_manager.delete_user('admin')
|
||||
self.auth_manager.delete_project('admin')
|
||||
"""Tear down test server."""
|
||||
self.server.stop()
|
||||
super(S3APITestCase, self).tearDown()
|
||||
|
||||
+27
-35
@@ -20,12 +20,9 @@ from nova import compute
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova import network
|
||||
from nova import quota
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova import volume
|
||||
from nova.auth import manager
|
||||
from nova.compute import instance_types
|
||||
|
||||
|
||||
@@ -48,25 +45,20 @@ class QuotaTestCase(test.TestCase):
|
||||
quota_gigabytes=20,
|
||||
quota_floating_ips=1)
|
||||
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
|
||||
self.project = self.manager.create_project('admin', 'admin', 'admin')
|
||||
self.network = self.network = self.start_service('network')
|
||||
self.context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
|
||||
def tearDown(self):
|
||||
manager.AuthManager().delete_project(self.project)
|
||||
manager.AuthManager().delete_user(self.user)
|
||||
super(QuotaTestCase, self).tearDown()
|
||||
self.user_id = 'admin'
|
||||
self.project_id = 'admin'
|
||||
self.context = context.RequestContext(self.user_id,
|
||||
self.project_id,
|
||||
True)
|
||||
|
||||
def _create_instance(self, cores=2):
|
||||
"""Create a test instance"""
|
||||
inst = {}
|
||||
inst['image_id'] = 1
|
||||
inst['reservation_id'] = 'r-fakeres'
|
||||
inst['user_id'] = self.user.id
|
||||
inst['project_id'] = self.project.id
|
||||
inst['user_id'] = self.user_id
|
||||
inst['project_id'] = self.project_id
|
||||
inst['instance_type_id'] = '3' # m1.large
|
||||
inst['vcpus'] = cores
|
||||
return db.instance_create(self.context, inst)['id']
|
||||
@@ -74,8 +66,8 @@ class QuotaTestCase(test.TestCase):
|
||||
def _create_volume(self, size=10):
|
||||
"""Create a test volume"""
|
||||
vol = {}
|
||||
vol['user_id'] = self.user.id
|
||||
vol['project_id'] = self.project.id
|
||||
vol['user_id'] = self.user_id
|
||||
vol['project_id'] = self.project_id
|
||||
vol['size'] = size
|
||||
return db.volume_create(self.context, vol)['id']
|
||||
|
||||
@@ -95,15 +87,15 @@ class QuotaTestCase(test.TestCase):
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
self._get_instance_type('m1.small'))
|
||||
self.assertEqual(num_instances, 2)
|
||||
db.quota_create(self.context, self.project.id, 'instances', 10)
|
||||
db.quota_create(self.context, self.project_id, 'instances', 10)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
self._get_instance_type('m1.small'))
|
||||
self.assertEqual(num_instances, 4)
|
||||
db.quota_create(self.context, self.project.id, 'cores', 100)
|
||||
db.quota_create(self.context, self.project_id, 'cores', 100)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
self._get_instance_type('m1.small'))
|
||||
self.assertEqual(num_instances, 10)
|
||||
db.quota_create(self.context, self.project.id, 'ram', 3 * 2048)
|
||||
db.quota_create(self.context, self.project_id, 'ram', 3 * 2048)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
self._get_instance_type('m1.small'))
|
||||
self.assertEqual(num_instances, 3)
|
||||
@@ -113,13 +105,13 @@ class QuotaTestCase(test.TestCase):
|
||||
num_metadata_items = quota.allowed_metadata_items(self.context,
|
||||
too_many_items)
|
||||
self.assertEqual(num_metadata_items, FLAGS.quota_metadata_items)
|
||||
db.quota_create(self.context, self.project.id, 'metadata_items', 5)
|
||||
db.quota_create(self.context, self.project_id, 'metadata_items', 5)
|
||||
num_metadata_items = quota.allowed_metadata_items(self.context,
|
||||
too_many_items)
|
||||
self.assertEqual(num_metadata_items, 5)
|
||||
|
||||
# Cleanup
|
||||
db.quota_destroy_all_by_project(self.context, self.project.id)
|
||||
db.quota_destroy_all_by_project(self.context, self.project_id)
|
||||
|
||||
def test_unlimited_instances(self):
|
||||
FLAGS.quota_instances = 2
|
||||
@@ -129,7 +121,7 @@ class QuotaTestCase(test.TestCase):
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 2)
|
||||
db.quota_create(self.context, self.project.id, 'instances', None)
|
||||
db.quota_create(self.context, self.project_id, 'instances', None)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 100)
|
||||
@@ -145,7 +137,7 @@ class QuotaTestCase(test.TestCase):
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 2)
|
||||
db.quota_create(self.context, self.project.id, 'ram', None)
|
||||
db.quota_create(self.context, self.project_id, 'ram', None)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 100)
|
||||
@@ -161,7 +153,7 @@ class QuotaTestCase(test.TestCase):
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 2)
|
||||
db.quota_create(self.context, self.project.id, 'cores', None)
|
||||
db.quota_create(self.context, self.project_id, 'cores', None)
|
||||
num_instances = quota.allowed_instances(self.context, 100,
|
||||
instance_type)
|
||||
self.assertEqual(num_instances, 100)
|
||||
@@ -174,7 +166,7 @@ class QuotaTestCase(test.TestCase):
|
||||
FLAGS.quota_gigabytes = -1
|
||||
volumes = quota.allowed_volumes(self.context, 100, 1)
|
||||
self.assertEqual(volumes, 10)
|
||||
db.quota_create(self.context, self.project.id, 'volumes', None)
|
||||
db.quota_create(self.context, self.project_id, 'volumes', None)
|
||||
volumes = quota.allowed_volumes(self.context, 100, 1)
|
||||
self.assertEqual(volumes, 100)
|
||||
volumes = quota.allowed_volumes(self.context, 101, 1)
|
||||
@@ -185,7 +177,7 @@ class QuotaTestCase(test.TestCase):
|
||||
FLAGS.quota_gigabytes = 10
|
||||
volumes = quota.allowed_volumes(self.context, 100, 1)
|
||||
self.assertEqual(volumes, 10)
|
||||
db.quota_create(self.context, self.project.id, 'gigabytes', None)
|
||||
db.quota_create(self.context, self.project_id, 'gigabytes', None)
|
||||
volumes = quota.allowed_volumes(self.context, 100, 1)
|
||||
self.assertEqual(volumes, 100)
|
||||
volumes = quota.allowed_volumes(self.context, 101, 1)
|
||||
@@ -195,7 +187,7 @@ class QuotaTestCase(test.TestCase):
|
||||
FLAGS.quota_floating_ips = 10
|
||||
floating_ips = quota.allowed_floating_ips(self.context, 100)
|
||||
self.assertEqual(floating_ips, 10)
|
||||
db.quota_create(self.context, self.project.id, 'floating_ips', None)
|
||||
db.quota_create(self.context, self.project_id, 'floating_ips', None)
|
||||
floating_ips = quota.allowed_floating_ips(self.context, 100)
|
||||
self.assertEqual(floating_ips, 100)
|
||||
floating_ips = quota.allowed_floating_ips(self.context, 101)
|
||||
@@ -205,7 +197,7 @@ class QuotaTestCase(test.TestCase):
|
||||
FLAGS.quota_metadata_items = 10
|
||||
items = quota.allowed_metadata_items(self.context, 100)
|
||||
self.assertEqual(items, 10)
|
||||
db.quota_create(self.context, self.project.id, 'metadata_items', None)
|
||||
db.quota_create(self.context, self.project_id, 'metadata_items', None)
|
||||
items = quota.allowed_metadata_items(self.context, 100)
|
||||
self.assertEqual(items, 100)
|
||||
items = quota.allowed_metadata_items(self.context, 101)
|
||||
@@ -273,11 +265,11 @@ class QuotaTestCase(test.TestCase):
|
||||
address = '192.168.0.100'
|
||||
db.floating_ip_create(context.get_admin_context(),
|
||||
{'address': address,
|
||||
'project_id': self.project.id})
|
||||
'project_id': self.project_id})
|
||||
self.assertRaises(quota.QuotaError,
|
||||
self.network.allocate_floating_ip,
|
||||
self.context,
|
||||
self.project.id)
|
||||
self.project_id)
|
||||
db.floating_ip_destroy(context.get_admin_context(), address)
|
||||
|
||||
def test_too_many_metadata_items(self):
|
||||
@@ -299,7 +291,7 @@ class QuotaTestCase(test.TestCase):
|
||||
|
||||
def test_overridden_allowed_injected_files(self):
|
||||
FLAGS.quota_max_injected_files = 5
|
||||
db.quota_create(self.context, self.project.id, 'injected_files', 77)
|
||||
db.quota_create(self.context, self.project_id, 'injected_files', 77)
|
||||
self.assertEqual(quota.allowed_injected_files(self.context, 100), 77)
|
||||
|
||||
def test_unlimited_default_allowed_injected_files(self):
|
||||
@@ -308,7 +300,7 @@ class QuotaTestCase(test.TestCase):
|
||||
|
||||
def test_unlimited_db_allowed_injected_files(self):
|
||||
FLAGS.quota_max_injected_files = 5
|
||||
db.quota_create(self.context, self.project.id, 'injected_files', None)
|
||||
db.quota_create(self.context, self.project_id, 'injected_files', None)
|
||||
self.assertEqual(quota.allowed_injected_files(self.context, 100), 100)
|
||||
|
||||
def test_default_allowed_injected_file_content_bytes(self):
|
||||
@@ -318,7 +310,7 @@ class QuotaTestCase(test.TestCase):
|
||||
|
||||
def test_overridden_allowed_injected_file_content_bytes(self):
|
||||
FLAGS.quota_max_injected_file_content_bytes = 12345
|
||||
db.quota_create(self.context, self.project.id,
|
||||
db.quota_create(self.context, self.project_id,
|
||||
'injected_file_content_bytes', 5678)
|
||||
limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
|
||||
self.assertEqual(limit, 5678)
|
||||
@@ -330,7 +322,7 @@ class QuotaTestCase(test.TestCase):
|
||||
|
||||
def test_unlimited_db_allowed_injected_file_content_bytes(self):
|
||||
FLAGS.quota_max_injected_file_content_bytes = 12345
|
||||
db.quota_create(self.context, self.project.id,
|
||||
db.quota_create(self.context, self.project_id,
|
||||
'injected_file_content_bytes', None)
|
||||
limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
|
||||
self.assertEqual(limit, 23456)
|
||||
|
||||
+12
-49
@@ -33,11 +33,12 @@ LOG = logging.getLogger('nova.tests.rpc')
|
||||
class RpcTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(RpcTestCase, self).setUp()
|
||||
self.conn = rpc.Connection.instance(True)
|
||||
self.conn = rpc.create_connection(True)
|
||||
self.receiver = TestReceiver()
|
||||
self.consumer = rpc.TopicAdapterConsumer(connection=self.conn,
|
||||
topic='test',
|
||||
proxy=self.receiver)
|
||||
self.consumer = rpc.create_consumer(self.conn,
|
||||
'test',
|
||||
self.receiver,
|
||||
False)
|
||||
self.consumer.attach_to_eventlet()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
@@ -129,6 +130,8 @@ class RpcTestCase(test.TestCase):
|
||||
"""Calls echo in the passed queue"""
|
||||
LOG.debug(_("Nested received %(queue)s, %(value)s")
|
||||
% locals())
|
||||
# TODO: so, it will replay the context and use the same REQID?
|
||||
# that's bizarre.
|
||||
ret = rpc.call(context,
|
||||
queue,
|
||||
{"method": "echo",
|
||||
@@ -137,10 +140,11 @@ class RpcTestCase(test.TestCase):
|
||||
return value
|
||||
|
||||
nested = Nested()
|
||||
conn = rpc.Connection.instance(True)
|
||||
consumer = rpc.TopicAdapterConsumer(connection=conn,
|
||||
topic='nested',
|
||||
proxy=nested)
|
||||
conn = rpc.create_connection(True)
|
||||
consumer = rpc.create_consumer(conn,
|
||||
'nested',
|
||||
nested,
|
||||
False)
|
||||
consumer.attach_to_eventlet()
|
||||
value = 42
|
||||
result = rpc.call(self.context,
|
||||
@@ -149,47 +153,6 @@ class RpcTestCase(test.TestCase):
|
||||
"value": value}})
|
||||
self.assertEqual(value, result)
|
||||
|
||||
def test_connectionpool_single(self):
|
||||
"""Test that ConnectionPool recycles a single connection."""
|
||||
conn1 = rpc.ConnectionPool.get()
|
||||
rpc.ConnectionPool.put(conn1)
|
||||
conn2 = rpc.ConnectionPool.get()
|
||||
rpc.ConnectionPool.put(conn2)
|
||||
self.assertEqual(conn1, conn2)
|
||||
|
||||
def test_connectionpool_double(self):
|
||||
"""Test that ConnectionPool returns and reuses separate connections.
|
||||
|
||||
When called consecutively we should get separate connections and upon
|
||||
returning them those connections should be reused for future calls
|
||||
before generating a new connection.
|
||||
|
||||
"""
|
||||
conn1 = rpc.ConnectionPool.get()
|
||||
conn2 = rpc.ConnectionPool.get()
|
||||
|
||||
self.assertNotEqual(conn1, conn2)
|
||||
rpc.ConnectionPool.put(conn1)
|
||||
rpc.ConnectionPool.put(conn2)
|
||||
|
||||
conn3 = rpc.ConnectionPool.get()
|
||||
conn4 = rpc.ConnectionPool.get()
|
||||
self.assertEqual(conn1, conn3)
|
||||
self.assertEqual(conn2, conn4)
|
||||
|
||||
def test_connectionpool_limit(self):
|
||||
"""Test connection pool limit and connection uniqueness."""
|
||||
max_size = FLAGS.rpc_conn_pool_size
|
||||
conns = []
|
||||
|
||||
for i in xrange(max_size):
|
||||
conns.append(rpc.ConnectionPool.get())
|
||||
|
||||
self.assertFalse(rpc.ConnectionPool.free_items)
|
||||
self.assertEqual(rpc.ConnectionPool.current_size,
|
||||
rpc.ConnectionPool.max_size)
|
||||
self.assertEqual(len(set(conns)), max_size)
|
||||
|
||||
|
||||
class TestReceiver(object):
|
||||
"""Simple Proxy class so the consumer has methods to call.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
from nova import context
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import rpc
|
||||
from nova.rpc import amqp
|
||||
from nova import test
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger('nova.tests.rpc')
|
||||
|
||||
|
||||
class RpcAMQPTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(RpcAMQPTestCase, self).setUp()
|
||||
self.conn = rpc.create_connection(True)
|
||||
self.receiver = TestReceiver()
|
||||
self.consumer = rpc.create_consumer(self.conn,
|
||||
'test',
|
||||
self.receiver,
|
||||
False)
|
||||
self.consumer.attach_to_eventlet()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
def test_connectionpool_single(self):
|
||||
"""Test that ConnectionPool recycles a single connection."""
|
||||
conn1 = amqp.ConnectionPool.get()
|
||||
amqp.ConnectionPool.put(conn1)
|
||||
conn2 = amqp.ConnectionPool.get()
|
||||
amqp.ConnectionPool.put(conn2)
|
||||
self.assertEqual(conn1, conn2)
|
||||
|
||||
|
||||
class TestReceiver(object):
|
||||
"""Simple Proxy class so the consumer has methods to call.
|
||||
|
||||
Uses static methods because we aren't actually storing any state.
|
||||
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def echo(context, value):
|
||||
"""Simply returns whatever value is sent in."""
|
||||
LOG.debug(_("Received %s"), value)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def context(context, value):
|
||||
"""Returns dictionary version of context."""
|
||||
LOG.debug(_("Received %s"), context)
|
||||
return context.to_dict()
|
||||
|
||||
@staticmethod
|
||||
def echo_three_times(context, value):
|
||||
context.reply(value)
|
||||
context.reply(value + 1)
|
||||
context.reply(value + 2)
|
||||
|
||||
@staticmethod
|
||||
def echo_three_times_yield(context, value):
|
||||
yield value
|
||||
yield value + 1
|
||||
yield value + 2
|
||||
|
||||
@staticmethod
|
||||
def fail(context, value):
|
||||
"""Raises an exception with the value sent in."""
|
||||
raise Exception(value)
|
||||
@@ -109,103 +109,8 @@ class ServiceTestCase(test.TestCase):
|
||||
# the looping calls are created in StartService.
|
||||
app = service.Service.create(host=host, binary=binary, topic=topic)
|
||||
|
||||
self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
|
||||
service.rpc.Connection.instance(new=mox.IgnoreArg())
|
||||
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'TopicAdapterConsumer',
|
||||
use_mock_anything=True)
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'FanoutAdapterConsumer',
|
||||
use_mock_anything=True)
|
||||
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'ConsumerSet',
|
||||
use_mock_anything=True)
|
||||
|
||||
rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic=topic,
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.TopicAdapterConsumer)
|
||||
|
||||
rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic='%s.%s' % (topic, host),
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.TopicAdapterConsumer)
|
||||
|
||||
rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic=topic,
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.FanoutAdapterConsumer)
|
||||
|
||||
def wait_func(self, limit=None):
|
||||
return None
|
||||
|
||||
mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
|
||||
{'wait': wait_func})
|
||||
rpc.ConsumerSet(connection=mox.IgnoreArg(),
|
||||
consumer_list=mox.IsA(list)).AndReturn(mock_cset)
|
||||
wait_func(mox.IgnoreArg())
|
||||
|
||||
service_create = {'host': host,
|
||||
'binary': binary,
|
||||
'topic': topic,
|
||||
'report_count': 0,
|
||||
'availability_zone': 'nova'}
|
||||
service_ref = {'host': host,
|
||||
'binary': binary,
|
||||
'report_count': 0,
|
||||
'id': 1}
|
||||
|
||||
service.db.service_get_by_args(mox.IgnoreArg(),
|
||||
host,
|
||||
binary).AndRaise(exception.NotFound())
|
||||
service.db.service_create(mox.IgnoreArg(),
|
||||
service_create).AndReturn(service_ref)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
app.start()
|
||||
app.stop()
|
||||
self.assert_(app)
|
||||
|
||||
# We're testing sort of weird behavior in how report_state decides
|
||||
# whether it is disconnected, it looks for a variable on itself called
|
||||
# 'model_disconnected' and report_state doesn't really do much so this
|
||||
# these are mostly just for coverage
|
||||
def test_report_state_no_service(self):
|
||||
host = 'foo'
|
||||
binary = 'bar'
|
||||
topic = 'test'
|
||||
service_create = {'host': host,
|
||||
'binary': binary,
|
||||
'topic': topic,
|
||||
'report_count': 0,
|
||||
'availability_zone': 'nova'}
|
||||
service_ref = {'host': host,
|
||||
'binary': binary,
|
||||
'topic': topic,
|
||||
'report_count': 0,
|
||||
'availability_zone': 'nova',
|
||||
'id': 1}
|
||||
|
||||
service.db.service_get_by_args(mox.IgnoreArg(),
|
||||
host,
|
||||
binary).AndRaise(exception.NotFound())
|
||||
service.db.service_create(mox.IgnoreArg(),
|
||||
service_create).AndReturn(service_ref)
|
||||
service.db.service_get(mox.IgnoreArg(),
|
||||
service_ref['id']).AndReturn(service_ref)
|
||||
service.db.service_update(mox.IgnoreArg(), service_ref['id'],
|
||||
mox.ContainsKeyValue('report_count', 1))
|
||||
|
||||
self.mox.ReplayAll()
|
||||
serv = service.Service(host,
|
||||
binary,
|
||||
topic,
|
||||
'nova.tests.test_service.FakeManager')
|
||||
serv.start()
|
||||
serv.report_state()
|
||||
|
||||
def test_report_state_newly_disconnected(self):
|
||||
host = 'foo'
|
||||
binary = 'bar'
|
||||
@@ -276,81 +181,6 @@ class ServiceTestCase(test.TestCase):
|
||||
|
||||
self.assert_(not serv.model_disconnected)
|
||||
|
||||
def test_compute_can_update_available_resource(self):
|
||||
"""Confirm compute updates their record of compute-service table."""
|
||||
host = 'foo'
|
||||
binary = 'nova-compute'
|
||||
topic = 'compute'
|
||||
|
||||
# Any mocks are not working without UnsetStubs() here.
|
||||
self.mox.UnsetStubs()
|
||||
ctxt = context.get_admin_context()
|
||||
service_ref = db.service_create(ctxt, {'host': host,
|
||||
'binary': binary,
|
||||
'topic': topic})
|
||||
serv = service.Service(host,
|
||||
binary,
|
||||
topic,
|
||||
'nova.compute.manager.ComputeManager')
|
||||
|
||||
# This testcase want to test calling update_available_resource.
|
||||
# No need to call periodic call, then below variable must be set 0.
|
||||
serv.report_interval = 0
|
||||
serv.periodic_interval = 0
|
||||
|
||||
# Creating mocks
|
||||
self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
|
||||
service.rpc.Connection.instance(new=mox.IgnoreArg())
|
||||
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'TopicAdapterConsumer',
|
||||
use_mock_anything=True)
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'FanoutAdapterConsumer',
|
||||
use_mock_anything=True)
|
||||
|
||||
self.mox.StubOutWithMock(rpc,
|
||||
'ConsumerSet',
|
||||
use_mock_anything=True)
|
||||
|
||||
rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic=topic,
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.TopicAdapterConsumer)
|
||||
|
||||
rpc.TopicAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic='%s.%s' % (topic, host),
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.TopicAdapterConsumer)
|
||||
|
||||
rpc.FanoutAdapterConsumer(connection=mox.IgnoreArg(),
|
||||
topic=topic,
|
||||
proxy=mox.IsA(service.Service)).AndReturn(
|
||||
rpc.FanoutAdapterConsumer)
|
||||
|
||||
def wait_func(self, limit=None):
|
||||
return None
|
||||
|
||||
mock_cset = self.mox.CreateMock(rpc.ConsumerSet,
|
||||
{'wait': wait_func})
|
||||
rpc.ConsumerSet(connection=mox.IgnoreArg(),
|
||||
consumer_list=mox.IsA(list)).AndReturn(mock_cset)
|
||||
wait_func(mox.IgnoreArg())
|
||||
|
||||
self.mox.StubOutWithMock(serv.manager.driver,
|
||||
'update_available_resource')
|
||||
serv.manager.driver.update_available_resource(mox.IgnoreArg(), host)
|
||||
|
||||
# Just doing start()-stop(), not confirm new db record is created,
|
||||
# because update_available_resource() works only in
|
||||
# libvirt environment. This testcase confirms
|
||||
# update_available_resource() is called. Otherwise, mox complains.
|
||||
self.mox.ReplayAll()
|
||||
serv.start()
|
||||
serv.stop()
|
||||
|
||||
db.service_destroy(ctxt, service_ref['id'])
|
||||
|
||||
|
||||
class TestWSGIService(test.TestCase):
|
||||
|
||||
|
||||
@@ -33,8 +33,13 @@ class IsolationTestCase(test.TestCase):
|
||||
self.start_service('compute')
|
||||
|
||||
def test_rpc_consumer_isolation(self):
|
||||
connection = rpc.Connection.instance(new=True)
|
||||
consumer = rpc.TopicAdapterConsumer(connection, topic='compute')
|
||||
consumer.register_callback(
|
||||
lambda x, y: self.fail('I should never be called'))
|
||||
class NeverCalled(object):
|
||||
|
||||
def __getattribute__(*args):
|
||||
assert False, "I should never get called."
|
||||
|
||||
connection = rpc.create_connection(new=True)
|
||||
proxy = NeverCalled()
|
||||
consumer = rpc.create_consumer(connection, 'compute',
|
||||
proxy, fanout=False)
|
||||
consumer.attach_to_eventlet()
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import StringIO
|
||||
import sys
|
||||
|
||||
from nova import twistd
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import test
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class TwistdTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TwistdTestCase, self).setUp()
|
||||
self.Options = twistd.WrapTwistedOptions(twistd.TwistdServerOptions)
|
||||
sys.stdout = StringIO.StringIO()
|
||||
|
||||
def tearDown(self):
|
||||
super(TwistdTestCase, self).tearDown()
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
def test_basic(self):
|
||||
options = self.Options()
|
||||
argv = options.parseOptions()
|
||||
|
||||
def test_logfile(self):
|
||||
options = self.Options()
|
||||
argv = options.parseOptions(['--logfile=foo'])
|
||||
self.assertEqual(FLAGS.logfile, 'foo')
|
||||
|
||||
def test_help(self):
|
||||
options = self.Options()
|
||||
self.assertRaises(SystemExit, options.parseOptions, ['--help'])
|
||||
self.assert_('pidfile' in sys.stdout.getvalue())
|
||||
@@ -14,6 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
@@ -306,3 +307,80 @@ class IsUUIDLikeTestCase(test.TestCase):
|
||||
def test_non_uuid_string_passed(self):
|
||||
val = 'foo-fooo'
|
||||
self.assertUUIDLike(val, False)
|
||||
|
||||
|
||||
class ToPrimitiveTestCase(test.TestCase):
|
||||
def test_list(self):
|
||||
self.assertEquals(utils.to_primitive([1, 2, 3]), [1, 2, 3])
|
||||
|
||||
def test_empty_list(self):
|
||||
self.assertEquals(utils.to_primitive([]), [])
|
||||
|
||||
def test_tuple(self):
|
||||
self.assertEquals(utils.to_primitive((1, 2, 3)), [1, 2, 3])
|
||||
|
||||
def test_dict(self):
|
||||
self.assertEquals(utils.to_primitive(dict(a=1, b=2, c=3)),
|
||||
dict(a=1, b=2, c=3))
|
||||
|
||||
def test_empty_dict(self):
|
||||
self.assertEquals(utils.to_primitive({}), {})
|
||||
|
||||
def test_datetime(self):
|
||||
x = datetime.datetime(1, 2, 3, 4, 5, 6, 7)
|
||||
self.assertEquals(utils.to_primitive(x), "0001-02-03 04:05:06.000007")
|
||||
|
||||
def test_iter(self):
|
||||
class IterClass(object):
|
||||
def __init__(self):
|
||||
self.data = [1, 2, 3, 4, 5]
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if self.index == len(self.data):
|
||||
raise StopIteration
|
||||
self.index = self.index + 1
|
||||
return self.data[self.index - 1]
|
||||
|
||||
x = IterClass()
|
||||
self.assertEquals(utils.to_primitive(x), [1, 2, 3, 4, 5])
|
||||
|
||||
def test_iteritems(self):
|
||||
class IterItemsClass(object):
|
||||
def __init__(self):
|
||||
self.data = dict(a=1, b=2, c=3).items()
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if self.index == len(self.data):
|
||||
raise StopIteration
|
||||
self.index = self.index + 1
|
||||
return self.data[self.index - 1]
|
||||
|
||||
x = IterItemsClass()
|
||||
ordered = utils.to_primitive(x)
|
||||
ordered.sort()
|
||||
self.assertEquals(ordered, [['a', 1], ['b', 2], ['c', 3]])
|
||||
|
||||
def test_instance(self):
|
||||
class MysteryClass(object):
|
||||
a = 10
|
||||
|
||||
def __init__(self):
|
||||
self.b = 1
|
||||
|
||||
x = MysteryClass()
|
||||
self.assertEquals(utils.to_primitive(x, convert_instances=True),
|
||||
dict(b=1))
|
||||
|
||||
self.assertEquals(utils.to_primitive(x), x)
|
||||
|
||||
def test_typeerror(self):
|
||||
x = bytearray # Class, not instance
|
||||
self.assertEquals(utils.to_primitive(x), u"<type 'bytearray'>")
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
Test suite for VMWareAPI.
|
||||
"""
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.compute import power_state
|
||||
from nova.tests.glance import stubs as glance_stubs
|
||||
from nova.tests.vmwareapi import db_fakes
|
||||
@@ -40,13 +40,13 @@ class VMWareAPIVMTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VMWareAPIVMTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
self.flags(vmwareapi_host_ip='test_url',
|
||||
vmwareapi_host_username='test_username',
|
||||
vmwareapi_host_password='test_pass')
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
vmwareapi_fake.reset()
|
||||
db_fakes.stub_out_db_instance_api(self.stubs)
|
||||
@@ -77,14 +77,12 @@ class VMWareAPIVMTestCase(test.TestCase):
|
||||
def tearDown(self):
|
||||
super(VMWareAPIVMTestCase, self).tearDown()
|
||||
vmwareapi_fake.cleanup()
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
|
||||
def _create_instance_in_the_db(self):
|
||||
values = {'name': 1,
|
||||
'id': 1,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'project_id': self.project_id,
|
||||
'user_id': self.user_id,
|
||||
'image_ref': "1",
|
||||
'kernel_id': "1",
|
||||
'ramdisk_id': "1",
|
||||
@@ -97,7 +95,7 @@ class VMWareAPIVMTestCase(test.TestCase):
|
||||
"""Create and spawn the VM."""
|
||||
self._create_instance_in_the_db()
|
||||
self.type_data = db.instance_type_get_by_name(None, 'm1.large')
|
||||
self.conn.spawn(self.instance, self.network_info)
|
||||
self.conn.spawn(self.context, self.instance, self.network_info)
|
||||
self._check_vm_record()
|
||||
|
||||
def _check_vm_record(self):
|
||||
@@ -159,14 +157,14 @@ class VMWareAPIVMTestCase(test.TestCase):
|
||||
self._create_vm()
|
||||
info = self.conn.get_info(1)
|
||||
self._check_vm_info(info, power_state.RUNNING)
|
||||
self.conn.snapshot(self.instance, "Test-Snapshot")
|
||||
self.conn.snapshot(self.context, self.instance, "Test-Snapshot")
|
||||
info = self.conn.get_info(1)
|
||||
self._check_vm_info(info, power_state.RUNNING)
|
||||
|
||||
def test_snapshot_non_existent(self):
|
||||
self._create_instance_in_the_db()
|
||||
self.assertRaises(Exception, self.conn.snapshot, self.instance,
|
||||
"Test-Snapshot")
|
||||
self.assertRaises(Exception, self.conn.snapshot, self.context,
|
||||
self.instance, "Test-Snapshot")
|
||||
|
||||
def test_reboot(self):
|
||||
self._create_vm()
|
||||
|
||||
+118
-68
@@ -30,7 +30,6 @@ from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.auth import manager
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
from nova import exception
|
||||
@@ -69,7 +68,9 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(XenAPIVolumeTestCase, self).setUp()
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
FLAGS.target_host = '127.0.0.1'
|
||||
FLAGS.xenapi_connection_url = 'test_url'
|
||||
FLAGS.xenapi_connection_password = 'test_pass'
|
||||
@@ -77,7 +78,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
stubs.stub_out_get_target(self.stubs)
|
||||
xenapi_fake.reset()
|
||||
self.values = {'id': 1,
|
||||
'project_id': 'fake',
|
||||
'project_id': self.user_id,
|
||||
'user_id': 'fake',
|
||||
'image_ref': 1,
|
||||
'kernel_id': 2,
|
||||
@@ -173,10 +174,6 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
"""Unit tests for VM operations."""
|
||||
def setUp(self):
|
||||
super(XenAPIVMTestCase, self).setUp()
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.network = utils.import_object(FLAGS.network_manager)
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
self.flags(xenapi_connection_url='test_url',
|
||||
@@ -195,7 +192,9 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
stubs.stub_out_vm_methods(self.stubs)
|
||||
glance_stubs.stubout_glance_client(self.stubs)
|
||||
fake_utils.stub_out_utils_execute(self.stubs)
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.conn = xenapi_conn.get_connection(False)
|
||||
|
||||
def test_parallel_builds(self):
|
||||
@@ -227,10 +226,10 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
instance = db.instance_create(self.context, values)
|
||||
self.conn.spawn(instance, network_info)
|
||||
self.conn.spawn(self.context, instance, network_info)
|
||||
|
||||
gt1 = eventlet.spawn(_do_build, 1, self.project.id, self.user.id)
|
||||
gt2 = eventlet.spawn(_do_build, 2, self.project.id, self.user.id)
|
||||
gt1 = eventlet.spawn(_do_build, 1, self.project_id, self.user_id)
|
||||
gt2 = eventlet.spawn(_do_build, 2, self.project_id, self.user_id)
|
||||
gt1.wait()
|
||||
gt2.wait()
|
||||
|
||||
@@ -257,14 +256,15 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
instance = self._create_instance()
|
||||
|
||||
name = "MySnapshot"
|
||||
self.assertRaises(exception.Error, self.conn.snapshot, instance, name)
|
||||
self.assertRaises(exception.Error, self.conn.snapshot,
|
||||
self.context, instance, name)
|
||||
|
||||
def test_instance_snapshot(self):
|
||||
stubs.stubout_instance_snapshot(self.stubs)
|
||||
instance = self._create_instance()
|
||||
|
||||
name = "MySnapshot"
|
||||
template_vm_ref = self.conn.snapshot(instance, name)
|
||||
template_vm_ref = self.conn.snapshot(self.context, instance, name)
|
||||
|
||||
def ensure_vm_was_torn_down():
|
||||
vm_labels = []
|
||||
@@ -401,8 +401,8 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
stubs.stubout_loopingcall_start(self.stubs)
|
||||
if create_record:
|
||||
values = {'id': instance_id,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'project_id': self.project_id,
|
||||
'user_id': self.user_id,
|
||||
'image_ref': image_ref,
|
||||
'kernel_id': kernel_id,
|
||||
'ramdisk_id': ramdisk_id,
|
||||
@@ -426,14 +426,13 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
'label': 'fake',
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
self.conn.spawn(instance, network_info)
|
||||
self.conn.spawn(self.context, instance, network_info)
|
||||
self.create_vm_record(self.conn, os_type, instance_id)
|
||||
self.check_vm_record(self.conn, check_injection)
|
||||
self.assertTrue(instance.os_type)
|
||||
self.assertTrue(instance.architecture)
|
||||
|
||||
def test_spawn_not_enough_memory(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self.assertRaises(Exception,
|
||||
self._test_spawn,
|
||||
1, 2, 3, "4") # m1.xlarge
|
||||
@@ -445,7 +444,6 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
|
||||
"""
|
||||
vdi_recs_start = self._list_vdis()
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
stubs.stubout_fetch_image_glance_disk(self.stubs)
|
||||
self.assertRaises(xenapi_fake.Failure,
|
||||
self._test_spawn, 1, 2, 3)
|
||||
@@ -460,7 +458,6 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
|
||||
"""
|
||||
vdi_recs_start = self._list_vdis()
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
stubs.stubout_create_vm(self.stubs)
|
||||
self.assertRaises(xenapi_fake.Failure,
|
||||
self._test_spawn, 1, 2, 3)
|
||||
@@ -468,22 +465,12 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
vdi_recs_end = self._list_vdis()
|
||||
self._check_vdis(vdi_recs_start, vdi_recs_end)
|
||||
|
||||
def test_spawn_raw_objectstore(self):
|
||||
FLAGS.xenapi_image_service = 'objectstore'
|
||||
self._test_spawn(1, None, None)
|
||||
|
||||
def test_spawn_objectstore(self):
|
||||
FLAGS.xenapi_image_service = 'objectstore'
|
||||
self._test_spawn(1, 2, 3)
|
||||
|
||||
@stub_vm_utils_with_vdi_attached_here
|
||||
def test_spawn_raw_glance(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_RAW, None, None)
|
||||
self.check_vm_params_for_linux()
|
||||
|
||||
def test_spawn_vhd_glance_linux(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None,
|
||||
os_type="linux", architecture="x86-64")
|
||||
self.check_vm_params_for_linux()
|
||||
@@ -512,20 +499,17 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.assertEqual(len(self.vm['VBDs']), 1)
|
||||
|
||||
def test_spawn_vhd_glance_windows(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None,
|
||||
os_type="windows", architecture="i386")
|
||||
self.check_vm_params_for_windows()
|
||||
|
||||
def test_spawn_glance(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
|
||||
glance_stubs.FakeGlance.IMAGE_KERNEL,
|
||||
glance_stubs.FakeGlance.IMAGE_RAMDISK)
|
||||
self.check_vm_params_for_linux_with_external_kernel()
|
||||
|
||||
def test_spawn_netinject_file(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
db_fakes.stub_out_db_instance_api(self.stubs, injected=True)
|
||||
|
||||
self._tee_executed = False
|
||||
@@ -551,7 +535,6 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
# Capture the sudo tee .../etc/network/interfaces command
|
||||
(r'(sudo\s+)?tee.*interfaces', _tee_handler),
|
||||
])
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
|
||||
glance_stubs.FakeGlance.IMAGE_KERNEL,
|
||||
glance_stubs.FakeGlance.IMAGE_RAMDISK,
|
||||
@@ -559,7 +542,6 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.assertTrue(self._tee_executed)
|
||||
|
||||
def test_spawn_netinject_xenstore(self):
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
db_fakes.stub_out_db_instance_api(self.stubs, injected=True)
|
||||
|
||||
self._tee_executed = False
|
||||
@@ -604,7 +586,7 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.assertFalse(self._tee_executed)
|
||||
|
||||
def test_spawn_vlanmanager(self):
|
||||
self.flags(xenapi_image_service='glance',
|
||||
self.flags(image_service='nova.image.glance.GlanceImageService',
|
||||
network_manager='nova.network.manager.VlanManager',
|
||||
vlan_interface='fake0')
|
||||
|
||||
@@ -626,7 +608,7 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
host=FLAGS.host,
|
||||
vpn=None,
|
||||
instance_type_id=1,
|
||||
project_id=self.project.id)
|
||||
project_id=self.project_id)
|
||||
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
|
||||
glance_stubs.FakeGlance.IMAGE_KERNEL,
|
||||
glance_stubs.FakeGlance.IMAGE_RAMDISK,
|
||||
@@ -648,7 +630,7 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.flags(flat_injected=False)
|
||||
instance = self._create_instance()
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
conn.rescue(instance, None, [])
|
||||
conn.rescue(self.context, instance, None, [])
|
||||
|
||||
def test_unrescue(self):
|
||||
instance = self._create_instance()
|
||||
@@ -656,21 +638,13 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
# Ensure that it will not unrescue a non-rescued instance.
|
||||
self.assertRaises(Exception, conn.unrescue, instance, None)
|
||||
|
||||
def tearDown(self):
|
||||
super(XenAPIVMTestCase, self).tearDown()
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
self.vm_info = None
|
||||
self.vm = None
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def _create_instance(self, instance_id=1, spawn=True):
|
||||
"""Creates and spawns a test instance."""
|
||||
stubs.stubout_loopingcall_start(self.stubs)
|
||||
values = {
|
||||
'id': instance_id,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'project_id': self.project_id,
|
||||
'user_id': self.user_id,
|
||||
'image_ref': 1,
|
||||
'kernel_id': 2,
|
||||
'ramdisk_id': 3,
|
||||
@@ -693,7 +667,7 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
if spawn:
|
||||
self.conn.spawn(instance, network_info)
|
||||
self.conn.spawn(self.context, instance, network_info)
|
||||
return instance
|
||||
|
||||
|
||||
@@ -752,14 +726,12 @@ class XenAPIMigrateInstance(test.TestCase):
|
||||
stubs.stub_out_get_target(self.stubs)
|
||||
xenapi_fake.reset()
|
||||
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
admin=True)
|
||||
self.project = self.manager.create_project('fake', 'fake', 'fake')
|
||||
self.context = context.RequestContext('fake', 'fake', False)
|
||||
self.user_id = 'fake'
|
||||
self.project_id = 'fake'
|
||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
||||
self.values = {'id': 1,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'project_id': self.project_id,
|
||||
'user_id': self.user_id,
|
||||
'image_ref': 1,
|
||||
'kernel_id': None,
|
||||
'ramdisk_id': None,
|
||||
@@ -773,20 +745,21 @@ class XenAPIMigrateInstance(test.TestCase):
|
||||
stubs.stubout_get_this_vm_uuid(self.stubs)
|
||||
glance_stubs.stubout_glance_client(self.stubs)
|
||||
|
||||
def tearDown(self):
|
||||
super(XenAPIMigrateInstance, self).tearDown()
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def test_migrate_disk_and_power_off(self):
|
||||
instance = db.instance_create(self.context, self.values)
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
conn.migrate_disk_and_power_off(instance, '127.0.0.1')
|
||||
|
||||
def test_finish_resize(self):
|
||||
def test_finish_migrate(self):
|
||||
instance = db.instance_create(self.context, self.values)
|
||||
self.called = False
|
||||
|
||||
def fake_vdi_resize(*args, **kwargs):
|
||||
self.called = True
|
||||
|
||||
self.stubs.Set(stubs.FakeSessionForMigrationTests,
|
||||
"VDI_resize_online", fake_vdi_resize)
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||
stubs.stubout_loopingcall_start(self.stubs)
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
@@ -804,8 +777,89 @@ class XenAPIMigrateInstance(test.TestCase):
|
||||
'label': 'fake',
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
conn.finish_resize(instance, dict(base_copy='hurr', cow='durr'),
|
||||
network_info)
|
||||
conn.finish_migration(self.context, instance,
|
||||
dict(base_copy='hurr', cow='durr'),
|
||||
network_info, resize_instance=True)
|
||||
self.assertEqual(self.called, True)
|
||||
|
||||
def test_finish_migrate_no_local_storage(self):
|
||||
tiny_type_id = \
|
||||
instance_types.get_instance_type_by_name('m1.tiny')['id']
|
||||
self.values.update({'instance_type_id': tiny_type_id, 'local_gb': 0})
|
||||
instance = db.instance_create(self.context, self.values)
|
||||
|
||||
def fake_vdi_resize(*args, **kwargs):
|
||||
raise Exception("This shouldn't be called")
|
||||
|
||||
self.stubs.Set(stubs.FakeSessionForMigrationTests,
|
||||
"VDI_resize_online", fake_vdi_resize)
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||
stubs.stubout_loopingcall_start(self.stubs)
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
|
||||
{'broadcast': '192.168.0.255',
|
||||
'dns': ['192.168.0.1'],
|
||||
'gateway': '192.168.0.1',
|
||||
'gateway6': 'dead:beef::1',
|
||||
'ip6s': [{'enabled': '1',
|
||||
'ip': 'dead:beef::dcad:beff:feef:0',
|
||||
'netmask': '64'}],
|
||||
'ips': [{'enabled': '1',
|
||||
'ip': '192.168.0.100',
|
||||
'netmask': '255.255.255.0'}],
|
||||
'label': 'fake',
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
conn.finish_migration(self.context, instance,
|
||||
dict(base_copy='hurr', cow='durr'),
|
||||
network_info, resize_instance=True)
|
||||
|
||||
def test_finish_migrate_no_resize_vdi(self):
|
||||
instance = db.instance_create(self.context, self.values)
|
||||
|
||||
def fake_vdi_resize(*args, **kwargs):
|
||||
raise Exception("This shouldn't be called")
|
||||
|
||||
self.stubs.Set(stubs.FakeSessionForMigrationTests,
|
||||
"VDI_resize_online", fake_vdi_resize)
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForMigrationTests)
|
||||
stubs.stubout_loopingcall_start(self.stubs)
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
network_info = [({'bridge': 'fa0', 'id': 0, 'injected': False},
|
||||
{'broadcast': '192.168.0.255',
|
||||
'dns': ['192.168.0.1'],
|
||||
'gateway': '192.168.0.1',
|
||||
'gateway6': 'dead:beef::1',
|
||||
'ip6s': [{'enabled': '1',
|
||||
'ip': 'dead:beef::dcad:beff:feef:0',
|
||||
'netmask': '64'}],
|
||||
'ips': [{'enabled': '1',
|
||||
'ip': '192.168.0.100',
|
||||
'netmask': '255.255.255.0'}],
|
||||
'label': 'fake',
|
||||
'mac': 'DE:AD:BE:EF:00:00',
|
||||
'rxtx_cap': 3})]
|
||||
|
||||
# Resize instance would be determined by the compute call
|
||||
conn.finish_migration(self.context, instance,
|
||||
dict(base_copy='hurr', cow='durr'),
|
||||
network_info, resize_instance=False)
|
||||
|
||||
|
||||
class XenAPIImageTypeTestCase(test.TestCase):
|
||||
"""Test ImageType class."""
|
||||
|
||||
def test_to_string(self):
|
||||
"""Can convert from type id to type string."""
|
||||
self.assertEquals(
|
||||
vm_utils.ImageType.to_string(vm_utils.ImageType.KERNEL),
|
||||
vm_utils.ImageType.KERNEL_STR)
|
||||
|
||||
def test_from_string(self):
|
||||
"""Can convert from string to type id."""
|
||||
self.assertEquals(
|
||||
vm_utils.ImageType.from_string(vm_utils.ImageType.KERNEL_STR),
|
||||
vm_utils.ImageType.KERNEL)
|
||||
|
||||
|
||||
class XenAPIDetermineDiskImageTestCase(test.TestCase):
|
||||
@@ -829,7 +883,6 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
|
||||
|
||||
def test_instance_disk(self):
|
||||
"""If a kernel is specified, the image type is DISK (aka machine)."""
|
||||
FLAGS.xenapi_image_service = 'objectstore'
|
||||
self.fake_instance.image_ref = glance_stubs.FakeGlance.IMAGE_MACHINE
|
||||
self.fake_instance.kernel_id = glance_stubs.FakeGlance.IMAGE_KERNEL
|
||||
self.assert_disk_type(vm_utils.ImageType.DISK)
|
||||
@@ -839,7 +892,6 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
|
||||
If the kernel isn't specified, and we're not using Glance, then
|
||||
DISK_RAW is assumed.
|
||||
"""
|
||||
FLAGS.xenapi_image_service = 'objectstore'
|
||||
self.fake_instance.image_ref = glance_stubs.FakeGlance.IMAGE_RAW
|
||||
self.fake_instance.kernel_id = None
|
||||
self.assert_disk_type(vm_utils.ImageType.DISK_RAW)
|
||||
@@ -849,7 +901,6 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
|
||||
If we're using Glance, then defer to the image_type field, which in
|
||||
this case will be 'raw'.
|
||||
"""
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self.fake_instance.image_ref = glance_stubs.FakeGlance.IMAGE_RAW
|
||||
self.fake_instance.kernel_id = None
|
||||
self.assert_disk_type(vm_utils.ImageType.DISK_RAW)
|
||||
@@ -859,7 +910,6 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
|
||||
If we're using Glance, then defer to the image_type field, which in
|
||||
this case will be 'vhd'.
|
||||
"""
|
||||
FLAGS.xenapi_image_service = 'glance'
|
||||
self.fake_instance.image_ref = glance_stubs.FakeGlance.IMAGE_VHD
|
||||
self.fake_instance.kernel_id = None
|
||||
self.assert_disk_type(vm_utils.ImageType.DISK_VHD)
|
||||
|
||||
@@ -28,8 +28,8 @@ from nova import utils
|
||||
|
||||
def stubout_instance_snapshot(stubs):
|
||||
@classmethod
|
||||
def fake_fetch_image(cls, session, instance_id, image, user, project,
|
||||
type):
|
||||
def fake_fetch_image(cls, context, session, instance_id, image, user,
|
||||
project, type):
|
||||
from nova.virt.xenapi.fake import create_vdi
|
||||
name_label = "instance-%s" % instance_id
|
||||
#TODO: create fake SR record
|
||||
@@ -227,7 +227,7 @@ def stub_out_vm_methods(stubs):
|
||||
def fake_release_bootlock(self, vm):
|
||||
pass
|
||||
|
||||
def fake_spawn_rescue(self, inst):
|
||||
def fake_spawn_rescue(self, context, inst, network_info):
|
||||
inst._rescue = False
|
||||
|
||||
stubs.Set(vmops.VMOps, "_shutdown", fake_shutdown)
|
||||
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Twisted daemon helpers, specifically to parse out gFlags from twisted flags,
|
||||
manage pid files and support syslogging.
|
||||
"""
|
||||
|
||||
import gflags
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
from twisted.scripts import twistd
|
||||
from twisted.python import log
|
||||
from twisted.python import reflect
|
||||
from twisted.python import runtime
|
||||
from twisted.python import usage
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
|
||||
if runtime.platformType == "win32":
|
||||
from twisted.scripts._twistw import ServerOptions
|
||||
else:
|
||||
from twisted.scripts._twistd_unix import ServerOptions
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class TwistdServerOptions(ServerOptions):
|
||||
def parseArgs(self, *args):
|
||||
return
|
||||
|
||||
|
||||
class FlagParser(object):
|
||||
# this is a required attribute for gflags
|
||||
syntactic_help = ''
|
||||
|
||||
def __init__(self, parser):
|
||||
self.parser = parser
|
||||
|
||||
def Parse(self, s):
|
||||
return self.parser(s)
|
||||
|
||||
|
||||
def WrapTwistedOptions(wrapped):
|
||||
class TwistedOptionsToFlags(wrapped):
|
||||
subCommands = None
|
||||
|
||||
def __init__(self):
|
||||
# NOTE(termie): _data exists because Twisted stuff expects
|
||||
# to be able to set arbitrary things that are
|
||||
# not actual flags
|
||||
self._data = {}
|
||||
self._flagHandlers = {}
|
||||
self._paramHandlers = {}
|
||||
|
||||
# Absorb the twistd flags into our FLAGS
|
||||
self._absorbFlags()
|
||||
self._absorbParameters()
|
||||
self._absorbHandlers()
|
||||
|
||||
wrapped.__init__(self)
|
||||
|
||||
def _absorbFlags(self):
|
||||
twistd_flags = []
|
||||
reflect.accumulateClassList(self.__class__, 'optFlags',
|
||||
twistd_flags)
|
||||
for flag in twistd_flags:
|
||||
key = flag[0].replace('-', '_')
|
||||
if hasattr(FLAGS, key):
|
||||
continue
|
||||
flags.DEFINE_boolean(key, None, str(flag[-1]))
|
||||
|
||||
def _absorbParameters(self):
|
||||
twistd_params = []
|
||||
reflect.accumulateClassList(self.__class__, 'optParameters',
|
||||
twistd_params)
|
||||
for param in twistd_params:
|
||||
key = param[0].replace('-', '_')
|
||||
if hasattr(FLAGS, key):
|
||||
continue
|
||||
if len(param) > 4:
|
||||
flags.DEFINE(FlagParser(param[4]),
|
||||
key, param[2], str(param[3]),
|
||||
serializer=gflags.ArgumentSerializer())
|
||||
else:
|
||||
flags.DEFINE_string(key, param[2], str(param[3]))
|
||||
|
||||
def _absorbHandlers(self):
|
||||
twistd_handlers = {}
|
||||
reflect.addMethodNamesToDict(self.__class__, twistd_handlers,
|
||||
"opt_")
|
||||
|
||||
# NOTE(termie): Much of the following is derived/copied from
|
||||
# twisted.python.usage with the express purpose of
|
||||
# providing compatibility
|
||||
for name in twistd_handlers.keys():
|
||||
method = getattr(self, 'opt_' + name)
|
||||
|
||||
takesArg = not usage.flagFunction(method, name)
|
||||
doc = getattr(method, '__doc__', None)
|
||||
if not doc:
|
||||
doc = 'undocumented'
|
||||
|
||||
if not takesArg:
|
||||
if name not in FLAGS:
|
||||
flags.DEFINE_boolean(name, None, doc)
|
||||
self._flagHandlers[name] = method
|
||||
else:
|
||||
if name not in FLAGS:
|
||||
flags.DEFINE_string(name, None, doc)
|
||||
self._paramHandlers[name] = method
|
||||
|
||||
def _doHandlers(self):
|
||||
for flag, handler in self._flagHandlers.iteritems():
|
||||
if self[flag]:
|
||||
handler()
|
||||
for param, handler in self._paramHandlers.iteritems():
|
||||
if self[param] is not None:
|
||||
handler(self[param])
|
||||
|
||||
def __str__(self):
|
||||
return str(FLAGS)
|
||||
|
||||
def parseOptions(self, options=None):
|
||||
if options is None:
|
||||
options = sys.argv
|
||||
else:
|
||||
options.insert(0, '')
|
||||
|
||||
args = FLAGS(options)
|
||||
logging.setup()
|
||||
argv = args[1:]
|
||||
# ignore subcommands
|
||||
|
||||
try:
|
||||
self.parseArgs(*argv)
|
||||
except TypeError:
|
||||
raise usage.UsageError(_("Wrong number of arguments."))
|
||||
|
||||
self.postOptions()
|
||||
return args
|
||||
|
||||
def parseArgs(self, *args):
|
||||
# TODO(termie): figure out a decent way of dealing with args
|
||||
#return
|
||||
wrapped.parseArgs(self, *args)
|
||||
|
||||
def postOptions(self):
|
||||
self._doHandlers()
|
||||
|
||||
wrapped.postOptions(self)
|
||||
|
||||
def __getitem__(self, key):
|
||||
key = key.replace('-', '_')
|
||||
try:
|
||||
return getattr(FLAGS, key)
|
||||
except (AttributeError, KeyError):
|
||||
return self._data[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = key.replace('-', '_')
|
||||
try:
|
||||
return setattr(FLAGS, key, value)
|
||||
except (AttributeError, KeyError):
|
||||
self._data[key] = value
|
||||
|
||||
def get(self, key, default):
|
||||
key = key.replace('-', '_')
|
||||
try:
|
||||
return getattr(FLAGS, key)
|
||||
except (AttributeError, KeyError):
|
||||
self._data.get(key, default)
|
||||
|
||||
return TwistedOptionsToFlags
|
||||
|
||||
|
||||
def stop(pidfile):
|
||||
"""
|
||||
Stop the daemon
|
||||
"""
|
||||
# Get the pid from the pidfile
|
||||
try:
|
||||
pf = file(pidfile, 'r')
|
||||
pid = int(pf.read().strip())
|
||||
pf.close()
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if not pid:
|
||||
message = _("pidfile %s does not exist. Daemon not running?\n")
|
||||
sys.stderr.write(message % pidfile)
|
||||
# Not an error in a restart
|
||||
return
|
||||
|
||||
# Try killing the daemon process
|
||||
try:
|
||||
while 1:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
time.sleep(0.1)
|
||||
except OSError, err:
|
||||
err = str(err)
|
||||
if err.find(_("No such process")) > 0:
|
||||
if os.path.exists(pidfile):
|
||||
os.remove(pidfile)
|
||||
else:
|
||||
print str(err)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def serve(filename):
|
||||
logging.debug(_("Serving %s") % filename)
|
||||
name = os.path.basename(filename)
|
||||
OptionsClass = WrapTwistedOptions(TwistdServerOptions)
|
||||
options = OptionsClass()
|
||||
argv = options.parseOptions()
|
||||
FLAGS.python = filename
|
||||
FLAGS.no_save = True
|
||||
if not FLAGS.pidfile:
|
||||
FLAGS.pidfile = '%s.pid' % name
|
||||
elif FLAGS.pidfile.endswith('twistd.pid'):
|
||||
FLAGS.pidfile = FLAGS.pidfile.replace('twistd.pid', '%s.pid' % name)
|
||||
if not FLAGS.prefix:
|
||||
FLAGS.prefix = name
|
||||
elif FLAGS.prefix.endswith('twisted'):
|
||||
FLAGS.prefix = FLAGS.prefix.replace('twisted', name)
|
||||
|
||||
action = 'start'
|
||||
if len(argv) > 1:
|
||||
action = argv.pop()
|
||||
|
||||
if action == 'stop':
|
||||
stop(FLAGS.pidfile)
|
||||
sys.exit()
|
||||
elif action == 'restart':
|
||||
stop(FLAGS.pidfile)
|
||||
elif action == 'start':
|
||||
pass
|
||||
else:
|
||||
print 'usage: %s [options] [start|stop|restart]' % argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
logging.debug(_("Full set of FLAGS:"))
|
||||
for flag in FLAGS:
|
||||
logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
|
||||
|
||||
logging.audit(_("Starting %s"), name)
|
||||
twistd.runApp(options)
|
||||
+71
-26
@@ -19,7 +19,6 @@
|
||||
|
||||
"""Utilities and helper functions."""
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import functools
|
||||
import inspect
|
||||
@@ -30,7 +29,6 @@ import os
|
||||
import random
|
||||
import re
|
||||
import socket
|
||||
import string
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
@@ -50,7 +48,8 @@ from nova import version
|
||||
|
||||
|
||||
LOG = logging.getLogger("nova.utils")
|
||||
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
ISO_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
||||
PERFECT_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
@@ -361,16 +360,26 @@ def clear_time_override():
|
||||
utcnow.override_time = None
|
||||
|
||||
|
||||
def isotime(at=None):
|
||||
"""Returns iso formatted utcnow."""
|
||||
def strtime(at=None, fmt=PERFECT_TIME_FORMAT):
|
||||
"""Returns formatted utcnow."""
|
||||
if not at:
|
||||
at = utcnow()
|
||||
return at.strftime(TIME_FORMAT)
|
||||
return at.strftime(fmt)
|
||||
|
||||
|
||||
def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT):
|
||||
"""Turn a formatted time back into a datetime."""
|
||||
return datetime.datetime.strptime(timestr, fmt)
|
||||
|
||||
|
||||
def isotime(at=None):
|
||||
"""Returns iso formatted utcnow."""
|
||||
return strtime(at, ISO_TIME_FORMAT)
|
||||
|
||||
|
||||
def parse_isotime(timestr):
|
||||
"""Turn an iso formatted time back into a datetime."""
|
||||
return datetime.datetime.strptime(timestr, TIME_FORMAT)
|
||||
return parse_strtime(timestr, ISO_TIME_FORMAT)
|
||||
|
||||
|
||||
def parse_mailmap(mailmap='.mailmap'):
|
||||
@@ -504,25 +513,61 @@ def utf8(value):
|
||||
return value
|
||||
|
||||
|
||||
def to_primitive(value):
|
||||
if type(value) is type([]) or type(value) is type((None,)):
|
||||
o = []
|
||||
for v in value:
|
||||
o.append(to_primitive(v))
|
||||
return o
|
||||
elif type(value) is type({}):
|
||||
o = {}
|
||||
for k, v in value.iteritems():
|
||||
o[k] = to_primitive(v)
|
||||
return o
|
||||
elif isinstance(value, datetime.datetime):
|
||||
return str(value)
|
||||
elif hasattr(value, 'iteritems'):
|
||||
return to_primitive(dict(value.iteritems()))
|
||||
elif hasattr(value, '__iter__'):
|
||||
return to_primitive(list(value))
|
||||
else:
|
||||
return value
|
||||
def to_primitive(value, convert_instances=False, level=0):
|
||||
"""Convert a complex object into primitives.
|
||||
|
||||
Handy for JSON serialization. We can optionally handle instances,
|
||||
but since this is a recursive function, we could have cyclical
|
||||
data structures.
|
||||
|
||||
To handle cyclical data structures we could track the actual objects
|
||||
visited in a set, but not all objects are hashable. Instead we just
|
||||
track the depth of the object inspections and don't go too deep.
|
||||
|
||||
Therefore, convert_instances=True is lossy ... be aware.
|
||||
|
||||
"""
|
||||
if inspect.isclass(value):
|
||||
return unicode(value)
|
||||
|
||||
if level > 3:
|
||||
return []
|
||||
|
||||
# The try block may not be necessary after the class check above,
|
||||
# but just in case ...
|
||||
try:
|
||||
if type(value) is type([]) or type(value) is type((None,)):
|
||||
o = []
|
||||
for v in value:
|
||||
o.append(to_primitive(v, convert_instances=convert_instances,
|
||||
level=level))
|
||||
return o
|
||||
elif type(value) is type({}):
|
||||
o = {}
|
||||
for k, v in value.iteritems():
|
||||
o[k] = to_primitive(v, convert_instances=convert_instances,
|
||||
level=level)
|
||||
return o
|
||||
elif isinstance(value, datetime.datetime):
|
||||
return str(value)
|
||||
elif hasattr(value, 'iteritems'):
|
||||
return to_primitive(dict(value.iteritems()),
|
||||
convert_instances=convert_instances,
|
||||
level=level)
|
||||
elif hasattr(value, '__iter__'):
|
||||
return to_primitive(list(value), level)
|
||||
elif convert_instances and hasattr(value, '__dict__'):
|
||||
# Likely an instance of something. Watch for cycles.
|
||||
# Ignore class member vars.
|
||||
return to_primitive(value.__dict__,
|
||||
convert_instances=convert_instances,
|
||||
level=level + 1)
|
||||
else:
|
||||
return value
|
||||
except TypeError, e:
|
||||
# Class objects are tricky since they may define something like
|
||||
# __iter__ defined but it isn't callable as list().
|
||||
return unicode(value)
|
||||
|
||||
|
||||
def dumps(value):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user