summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSandy Walsh <sandy.walsh@rackspace.com>2011-05-23 05:30:44 -0700
committerSandy Walsh <sandy.walsh@rackspace.com>2011-05-23 05:30:44 -0700
commitbf0491ec2ee1d24de0e5f3a1d10f9ab45f639f2a (patch)
tree6640271b519324fe215ef75b155356bd162e9ce7
parent40f15a6ffb0d9ae965f9c7c7289654f323f2775f (diff)
parent4f8f5cb40f831c49af11cd801e1766fd6f0956c3 (diff)
Trunk merge
-rw-r--r--Authors1
-rw-r--r--MANIFEST.in1
-rwxr-xr-xbin/nova-manage67
-rwxr-xr-xbin/stack2
-rw-r--r--nova/api/ec2/cloud.py23
-rw-r--r--nova/api/openstack/__init__.py2
-rw-r--r--nova/api/openstack/auth.py17
-rw-r--r--nova/api/openstack/extensions.py84
-rw-r--r--nova/api/openstack/zones.py13
-rw-r--r--nova/compute/manager.py2
-rw-r--r--nova/db/sqlalchemy/api.py2
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/017_make_instance_type_id_an_integer.py68
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/018_rename_server_management_url.py60
-rw-r--r--nova/db/sqlalchemy/models.py4
-rw-r--r--nova/exception.py4
-rw-r--r--nova/flags.py3
-rw-r--r--nova/network/api.py45
-rw-r--r--nova/network/linux_net.py228
-rw-r--r--nova/network/manager.py67
-rw-r--r--nova/network/vmwareapi_net.py14
-rw-r--r--nova/network/xenapi_net.py18
-rw-r--r--nova/notifier/__init__.py14
-rw-r--r--nova/notifier/api.py83
-rw-r--r--nova/notifier/log_notifier.py34
-rw-r--r--nova/notifier/no_op_notifier.py19
-rw-r--r--nova/notifier/rabbit_notifier.py36
-rw-r--r--nova/service.py10
-rw-r--r--nova/tests/api/openstack/extensions/foxinsocks.py26
-rw-r--r--nova/tests/api/openstack/fakes.py3
-rw-r--r--nova/tests/api/openstack/test_extensions.py45
-rw-r--r--nova/tests/api/openstack/test_zones.py1
-rw-r--r--nova/tests/integrated/integrated_helpers.py13
-rw-r--r--nova/tests/public_key/dummy.fingerprint1
-rw-r--r--nova/tests/public_key/dummy.pub1
-rw-r--r--nova/tests/test_cloud.py30
-rw-r--r--nova/tests/test_compute.py22
-rw-r--r--nova/tests/test_notifier.py117
-rw-r--r--nova/tests/test_scheduler.py61
-rw-r--r--nova/tests/test_virt.py12
-rw-r--r--nova/virt/disk.py50
-rw-r--r--nova/virt/images.py69
-rw-r--r--nova/virt/libvirt_conn.py20
-rw-r--r--nova/virt/xenapi/vm_utils.py8
-rw-r--r--nova/virt/xenapi/vmops.py10
-rw-r--r--nova/virt/xenapi_conn.py8
-rw-r--r--nova/wsgi.py5
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/glance4
-rw-r--r--plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost183
-rwxr-xr-xrun_tests.sh6
-rw-r--r--tools/install_venv.py174
50 files changed, 1280 insertions, 510 deletions
diff --git a/Authors b/Authors
index 546c9091f..6741c81ff 100644
--- a/Authors
+++ b/Authors
@@ -1,4 +1,5 @@
Alex Meade <alex.meade@rackspace.com>
+Andrey Brindeyev <abrindeyev@griddynamics.com>
Andy Smith <code@term.ie>
Andy Southgate <andy.southgate@citrix.com>
Anne Gentle <anne@openstack.org>
diff --git a/MANIFEST.in b/MANIFEST.in
index e7a6e7da4..4e145de75 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -35,6 +35,7 @@ include nova/tests/bundle/1mb.manifest.xml
include nova/tests/bundle/1mb.no_kernel_or_ramdisk.manifest.xml
include nova/tests/bundle/1mb.part.0
include nova/tests/bundle/1mb.part.1
+include nova/tests/public_key/*
include nova/tests/db/nova.austin.sqlite
include plugins/xenapi/README
include plugins/xenapi/etc/xapi.d/plugins/objectstore
diff --git a/bin/nova-manage b/bin/nova-manage
index c95b216ce..e3ed7b9d0 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -362,27 +362,47 @@ class ProjectCommands(object):
def add(self, project_id, user_id):
"""Adds user to project
arguments: project_id user_id"""
- self.manager.add_to_project(user_id, project_id)
+ try:
+ self.manager.add_to_project(user_id, project_id)
+ except exception.UserNotFound as ex:
+ print ex
+ raise
def create(self, name, project_manager, description=None):
"""Creates a new project
arguments: name project_manager [description]"""
- self.manager.create_project(name, project_manager, description)
+ try:
+ self.manager.create_project(name, project_manager, description)
+ except exception.UserNotFound as ex:
+ print ex
+ raise
def modify(self, name, project_manager, description=None):
"""Modifies a project
arguments: name project_manager [description]"""
- self.manager.modify_project(name, project_manager, description)
+ try:
+ self.manager.modify_project(name, project_manager, description)
+ except exception.UserNotFound as ex:
+ print ex
+ raise
def delete(self, name):
"""Deletes an existing project
arguments: name"""
- self.manager.delete_project(name)
+ try:
+ self.manager.delete_project(name)
+ except exception.ProjectNotFound as ex:
+ print ex
+ raise
def environment(self, project_id, user_id, filename='novarc'):
"""Exports environment variables to an sourcable file
arguments: project_id user_id [filename='novarc]"""
- rc = self.manager.get_environment_rc(user_id, project_id)
+ try:
+ rc = self.manager.get_environment_rc(user_id, project_id)
+ except (exception.UserNotFound, exception.ProjectNotFound) as ex:
+ print ex
+ raise
with open(filename, 'w') as f:
f.write(rc)
@@ -399,7 +419,7 @@ class ProjectCommands(object):
if key:
try:
db.quota_update(ctxt, project_id, key, value)
- except exception.NotFound:
+ except exception.ProjectQuotaNotFound:
db.quota_create(ctxt, project_id, key, value)
project_quota = quota.get_quota(ctxt, project_id)
for key, value in project_quota.iteritems():
@@ -408,7 +428,11 @@ class ProjectCommands(object):
def remove(self, project_id, user_id):
"""Removes user from project
arguments: project_id user_id"""
- self.manager.remove_from_project(user_id, project_id)
+ try:
+ self.manager.remove_from_project(user_id, project_id)
+ except (exception.UserNotFound, exception.ProjectNotFound) as ex:
+ print ex
+ raise
def scrub(self, project_id):
"""Deletes data associated with project
@@ -427,6 +451,9 @@ class ProjectCommands(object):
zip_file = self.manager.get_credentials(user_id, project_id)
with open(filename, 'w') as f:
f.write(zip_file)
+ except (exception.UserNotFound, exception.ProjectNotFound) as ex:
+ print ex
+ raise
except db.api.NoMoreNetworks:
print _('No more networks available. If this is a new '
'installation, you need\nto call something like this:\n\n'
@@ -522,8 +549,10 @@ class NetworkCommands(object):
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
- raise TypeError(_('Fixed range in the form of 10.0.0.0/8 is '
- 'required to create networks.'))
+ msg = _('Fixed range in the form of 10.0.0.0/8 is '
+ 'required to create networks.')
+ print msg
+ raise TypeError(msg)
if not num_networks:
num_networks = FLAGS.num_networks
if not network_size:
@@ -535,14 +564,18 @@ class NetworkCommands(object):
if not fixed_range_v6:
fixed_range_v6 = FLAGS.fixed_range_v6
net_manager = utils.import_object(FLAGS.network_manager)
- net_manager.create_networks(context.get_admin_context(),
- cidr=fixed_range,
- num_networks=int(num_networks),
- network_size=int(network_size),
- vlan_start=int(vlan_start),
- vpn_start=int(vpn_start),
- cidr_v6=fixed_range_v6,
- label=label)
+ try:
+ net_manager.create_networks(context.get_admin_context(),
+ cidr=fixed_range,
+ num_networks=int(num_networks),
+ network_size=int(network_size),
+ vlan_start=int(vlan_start),
+ vpn_start=int(vpn_start),
+ cidr_v6=fixed_range_v6,
+ label=label)
+ except ValueError, e:
+ print e
+ raise e
def list(self):
"""List all created networks"""
diff --git a/bin/stack b/bin/stack
index d84a82e27..a1c6d1348 100755
--- a/bin/stack
+++ b/bin/stack
@@ -65,7 +65,7 @@ def format_help(d):
indent = MAX_INDENT - 6
out = []
- for k, v in d.iteritems():
+ for k, v in sorted(d.iteritems()):
if (len(k) + 6) > MAX_INDENT:
out.extend([' %s' % k])
initial_indent = ' ' * (indent + 6)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 1fa07d042..c35b6024e 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -27,6 +27,8 @@ import datetime
import IPy
import os
import urllib
+import tempfile
+import shutil
from nova import compute
from nova import context
@@ -316,6 +318,27 @@ class CloudController(object):
'keyMaterial': data['private_key']}
# TODO(vish): when context is no longer an object, pass it here
+ def import_public_key(self, context, key_name, public_key,
+ fingerprint=None):
+ LOG.audit(_("Import key %s"), key_name, context=context)
+ key = {}
+ key['user_id'] = context.user_id
+ key['name'] = key_name
+ key['public_key'] = public_key
+ if fingerprint is None:
+ tmpdir = tempfile.mkdtemp()
+ pubfile = os.path.join(tmpdir, 'temp.pub')
+ fh = open(pubfile, 'w')
+ fh.write(public_key)
+ fh.close()
+ (out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f',
+ '%s' % (pubfile))
+ fingerprint = out.split(' ')[1]
+ shutil.rmtree(tmpdir)
+ key['fingerprint'] = fingerprint
+ db.key_pair_create(context, key)
+ return True
+
def delete_key_pair(self, context, key_name, **kwargs):
LOG.audit(_("Delete key pair %s"), key_name, context=context)
try:
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 5d45efde6..5b7f080ad 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -99,7 +99,7 @@ class APIRouter(wsgi.Router):
mapper.resource("zone", "zones", controller=zones.Controller(),
collection={'detail': 'GET', 'info': 'GET',
- 'select': 'GET'}),
+ 'select': 'GET'})
mapper.resource("user", "users", controller=users.Controller(),
collection={'detail': 'GET'})
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 311e6bde9..6c6ee22a2 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -17,7 +17,6 @@
import datetime
import hashlib
-import json
import time
import webob.exc
@@ -25,11 +24,9 @@ import webob.dec
from nova import auth
from nova import context
-from nova import db
from nova import exception
from nova import flags
from nova import log as logging
-from nova import manager
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
@@ -102,11 +99,11 @@ class AuthMiddleware(wsgi.Middleware):
token, user = self._authorize_user(username, key, req)
if user and token:
res = webob.Response()
- res.headers['X-Auth-Token'] = token.token_hash
+ res.headers['X-Auth-Token'] = token['token_hash']
res.headers['X-Server-Management-Url'] = \
- token.server_management_url
- res.headers['X-Storage-Url'] = token.storage_url
- res.headers['X-CDN-Management-Url'] = token.cdn_management_url
+ token['server_management_url']
+ res.headers['X-Storage-Url'] = token['storage_url']
+ res.headers['X-CDN-Management-Url'] = token['cdn_management_url']
res.content_type = 'text/plain'
res.status = '204'
LOG.debug(_("Successfully authenticated '%s'") % username)
@@ -130,11 +127,11 @@ class AuthMiddleware(wsgi.Middleware):
except exception.NotFound:
return None
if token:
- delta = datetime.datetime.now() - token.created_at
+ delta = datetime.datetime.utcnow() - token['created_at']
if delta.days >= 2:
- self.db.auth_token_destroy(ctxt, token.token_hash)
+ self.db.auth_token_destroy(ctxt, token['token_hash'])
else:
- return self.auth.get_user(token.user_id)
+ return self.auth.get_user(token['user_id'])
return None
def _authorize_user(self, username, key, req):
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 7ea7afef6..8e77b25fb 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -105,15 +105,14 @@ class ExtensionDescriptor(object):
actions = []
return actions
- def get_response_extensions(self):
- """List of extensions.ResponseExtension extension objects.
+ def get_request_extensions(self):
+ """List of extensions.RequestException extension objects.
- Response extensions are used to insert information into existing
- response data.
+ Request extensions are used to handle custom request data.
"""
- response_exts = []
- return response_exts
+ request_exts = []
+ return request_exts
class ActionExtensionController(common.OpenstackController):
@@ -137,7 +136,7 @@ class ActionExtensionController(common.OpenstackController):
return res
-class ResponseExtensionController(common.OpenstackController):
+class RequestExtensionController(common.OpenstackController):
def __init__(self, application):
self.application = application
@@ -148,20 +147,9 @@ class ResponseExtensionController(common.OpenstackController):
def process(self, req, *args, **kwargs):
res = req.get_response(self.application)
- content_type = req.best_match_content_type()
- # currently response handlers are un-ordered
+ # currently request handlers are un-ordered
for handler in self.handlers:
- res = handler(res)
- try:
- body = res.body
- headers = res.headers
- except AttributeError:
- default_xmlns = None
- body = self._serialize(res, content_type, default_xmlns)
- headers = {"Content-Type": content_type}
- res = webob.Response()
- res.body = body
- res.headers = headers
+ res = handler(req, res)
return res
@@ -226,24 +214,24 @@ class ExtensionMiddleware(wsgi.Middleware):
return action_controllers
- def _response_ext_controllers(self, application, ext_mgr, mapper):
- """Returns a dict of ResponseExtensionController-s by collection."""
- response_ext_controllers = {}
- for resp_ext in ext_mgr.get_response_extensions():
- if not resp_ext.key in response_ext_controllers.keys():
- controller = ResponseExtensionController(application)
- mapper.connect(resp_ext.url_route + '.:(format)',
+ def _request_ext_controllers(self, application, ext_mgr, mapper):
+ """Returns a dict of RequestExtensionController-s by collection."""
+ request_ext_controllers = {}
+ for req_ext in ext_mgr.get_request_extensions():
+ if not req_ext.key in request_ext_controllers.keys():
+ controller = RequestExtensionController(application)
+ mapper.connect(req_ext.url_route + '.:(format)',
action='process',
controller=controller,
- conditions=resp_ext.conditions)
+ conditions=req_ext.conditions)
- mapper.connect(resp_ext.url_route,
+ mapper.connect(req_ext.url_route,
action='process',
controller=controller,
- conditions=resp_ext.conditions)
- response_ext_controllers[resp_ext.key] = controller
+ conditions=req_ext.conditions)
+ request_ext_controllers[req_ext.key] = controller
- return response_ext_controllers
+ return request_ext_controllers
def __init__(self, application, ext_mgr=None):
@@ -271,13 +259,13 @@ class ExtensionMiddleware(wsgi.Middleware):
controller = action_controllers[action.collection]
controller.add_action(action.action_name, action.handler)
- # extended responses
- resp_controllers = self._response_ext_controllers(application, ext_mgr,
+ # extended requests
+ req_controllers = self._request_ext_controllers(application, ext_mgr,
mapper)
- for response_ext in ext_mgr.get_response_extensions():
- LOG.debug(_('Extended response: %s'), response_ext.key)
- controller = resp_controllers[response_ext.key]
- controller.add_handler(response_ext.handler)
+ for request_ext in ext_mgr.get_request_extensions():
+ LOG.debug(_('Extended request: %s'), request_ext.key)
+ controller = req_controllers[request_ext.key]
+ controller.add_handler(request_ext.handler)
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
mapper)
@@ -347,17 +335,17 @@ class ExtensionManager(object):
pass
return actions
- def get_response_extensions(self):
- """Returns a list of ResponseExtension objects."""
- response_exts = []
+ def get_request_extensions(self):
+ """Returns a list of RequestExtension objects."""
+ request_exts = []
for alias, ext in self.extensions.iteritems():
try:
- response_exts.extend(ext.get_response_extensions())
+ request_exts.extend(ext.get_request_extensions())
except AttributeError:
- # NOTE(dprince): Extension aren't required to have response
+ # NOTE(dprince): Extension aren't required to have request
# extensions
pass
- return response_exts
+ return request_exts
def _check_extension(self, extension):
"""Checks for required methods in extension objects."""
@@ -421,9 +409,13 @@ class ExtensionManager(object):
self.extensions[alias] = ext
-class ResponseExtension(object):
- """Add data to responses from core nova OpenStack API controllers."""
+class RequestExtension(object):
+ """Extend requests and responses of core nova OpenStack API controllers.
+ Provide a way to add data to responses and handle custom request data
+ that is sent to core nova OpenStack API controllers.
+
+ """
def __init__(self, method, url_route, handler):
self.url_route = url_route
self.handler = handler
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index 70653dc0e..af73d8f6d 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -18,6 +18,7 @@ import urlparse
from nova import crypto
from nova import db
+from nova import exception
from nova import flags
from nova import log as logging
from nova.api.openstack import common
@@ -51,14 +52,6 @@ def _scrub_zone(zone):
'deleted', 'deleted_at', 'updated_at'))
-def check_encryption_key(func):
- def wrapped(*args, **kwargs):
- if not FLAGS.build_plan_encryption_key:
- raise exception.Error(_("--build_plan_encryption_key not set"))
- return func(*args, **kwargs)
- return wrapped
-
-
class Controller(common.OpenstackController):
_serialization_metadata = {
@@ -116,7 +109,6 @@ class Controller(common.OpenstackController):
zone = api.zone_update(context, zone_id, env["zone"])
return dict(zone=_scrub_zone(zone))
- @check_encryption_key
def select(self, req):
"""Returns a weighted list of costs to create instances
of desired capabilities."""
@@ -137,6 +129,9 @@ class Controller(common.OpenstackController):
"""Remove all the confidential data and return a sanitized
version of the build plan. Include an encrypted full version
of the weighting entry so we can get back to it later."""
+ if not FLAGS.build_plan_encryption_key:
+ raise exception.FlagNotSet(flag='build_plan_encryption_key')
+
encryptor = crypto.encryptor(FLAGS.build_plan_encryption_key)
cooked = []
for entry in build_plan:
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 923feaa59..11565c25e 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -628,7 +628,7 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_type = self.db.instance_type_get_by_flavor_id(context,
migration_ref['new_flavor_id'])
self.db.instance_update(context, instance_id,
- dict(instance_type=instance_type['name'],
+ dict(instance_type_id=instance_type['id'],
memory_mb=instance_type['memory_mb'],
vcpus=instance_type['vcpus'],
local_gb=instance_type['local_gb']))
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 3681f30db..b53e81053 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -873,6 +873,7 @@ def instance_get_all(context):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
all()
@@ -885,6 +886,7 @@ def instance_get_all_by_user(context, user_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
+ options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
filter_by(user_id=user_id).\
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/017_make_instance_type_id_an_integer.py b/nova/db/sqlalchemy/migrate_repo/versions/017_make_instance_type_id_an_integer.py
new file mode 100644
index 000000000..cda890c94
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/017_make_instance_type_id_an_integer.py
@@ -0,0 +1,68 @@
+from sqlalchemy import Column, Integer, MetaData, String, Table
+from nova import log as logging
+
+meta = MetaData()
+
+
+def upgrade(migrate_engine):
+ meta.bind = migrate_engine
+ instances = Table('instances', meta, autoload=True,
+ autoload_with=migrate_engine)
+
+ types = {}
+ for instance in migrate_engine.execute(instances.select()):
+ if instance.instance_type_id is None:
+ types[instance.id] = None
+ continue
+ try:
+ types[instance.id] = int(instance.instance_type_id)
+ except ValueError:
+ logging.warn("Instance %s did not have instance_type_id "
+ "converted to an integer because its value is %s" %
+ (instance.id, instance.instance_type_id))
+ types[instance.id] = None
+
+ integer_column = Column('instance_type_id_int', Integer(), nullable=True)
+ string_column = instances.c.instance_type_id
+
+ integer_column.create(instances)
+ for instance_id, instance_type_id in types.iteritems():
+ update = instances.update().\
+ where(instances.c.id == instance_id).\
+ values(instance_type_id_int=instance_type_id)
+ migrate_engine.execute(update)
+
+ string_column.alter(name='instance_type_id_str')
+ integer_column.alter(name='instance_type_id')
+ string_column.drop()
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+ instances = Table('instances', meta, autoload=True,
+ autoload_with=migrate_engine)
+
+ integer_column = instances.c.instance_type_id
+ string_column = Column('instance_type_id_str',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+ types = {}
+ for instance in migrate_engine.execute(instances.select()):
+ if instance.instance_type_id is None:
+ types[instance.id] = None
+ else:
+ types[instance.id] = str(instance.instance_type_id)
+
+ string_column.create(instances)
+ for instance_id, instance_type_id in types.iteritems():
+ update = instances.update().\
+ where(instances.c.id == instance_id).\
+ values(instance_type_id_str=instance_type_id)
+ migrate_engine.execute(update)
+
+ integer_column.alter(name='instance_type_id_int')
+ string_column.alter(name='instance_type_id')
+ integer_column.drop()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/018_rename_server_management_url.py b/nova/db/sqlalchemy/migrate_repo/versions/018_rename_server_management_url.py
new file mode 100644
index 000000000..a169afb40
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/018_rename_server_management_url.py
@@ -0,0 +1,60 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+#
+# 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 sqlalchemy import Column, Integer, MetaData, String, Table
+#from nova import log as logging
+
+meta = MetaData()
+
+c_manageent = Column('server_manageent_url',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+c_management = Column('server_management_url',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ tokens = Table('auth_tokens', meta, autoload=True,
+ autoload_with=migrate_engine)
+
+ tokens.create_column(c_management)
+ migrate_engine.execute(tokens.update()
+ .values(server_management_url=tokens.c.server_manageent_url))
+
+ tokens.c.server_manageent_url.drop()
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ tokens = Table('auth_tokens', meta, autoload=True,
+ autoload_with=migrate_engine)
+
+ tokens.create_column(c_manageent)
+ migrate_engine.execute(tokens.update()
+ .values(server_manageent_url=tokens.c.server_management_url))
+
+ tokens.c.server_management_url.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 0b46d5a05..1215448f8 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -209,7 +209,7 @@ class Instance(BASE, NovaBase):
hostname = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
- instance_type_id = Column(String(255))
+ instance_type_id = Column(Integer)
user_data = Column(Text)
@@ -495,7 +495,7 @@ class AuthToken(BASE, NovaBase):
__tablename__ = 'auth_tokens'
token_hash = Column(String(255), primary_key=True)
user_id = Column(String(255))
- server_manageent_url = Column(String(255))
+ server_management_url = Column(String(255))
storage_url = Column(String(255))
cdn_management_url = Column(String(255))
diff --git a/nova/exception.py b/nova/exception.py
index cf6069454..56c20d111 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -255,6 +255,10 @@ class NotFound(NovaException):
super(NotFound, self).__init__(**kwargs)
+class FlagNotSet(NotFound):
+ message = _("Required flag %(flag)s not set.")
+
+
class InstanceNotFound(NotFound):
message = _("Instance %(instance_id)s could not be found.")
diff --git a/nova/flags.py b/nova/flags.py
index 519793643..32cb6efa8 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -369,6 +369,9 @@ DEFINE_string('host', socket.gethostname(),
DEFINE_string('node_availability_zone', 'nova',
'availability zone of this node')
+DEFINE_string('notification_driver',
+ 'nova.notifier.no_op_notifier',
+ 'Default driver for sending notifications')
DEFINE_list('memcached_servers', None,
'Memcached servers or None for in process cache.')
diff --git a/nova/network/api.py b/nova/network/api.py
index 1d8193b28..e2eacdf42 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -16,9 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Handles all requests relating to instances (guest vms).
-"""
+"""Handles all requests relating to instances (guest vms)."""
from nova import db
from nova import exception
@@ -28,6 +26,7 @@ from nova import quota
from nova import rpc
from nova.db import base
+
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.network')
@@ -37,19 +36,19 @@ class API(base.Base):
def allocate_floating_ip(self, context):
if quota.allowed_floating_ips(context, 1) < 1:
- LOG.warn(_("Quota exceeeded for %s, tried to allocate "
- "address"),
- context.project_id)
- raise quota.QuotaError(_("Address quota exceeded. You cannot "
- "allocate any more addresses"))
+ LOG.warn(_('Quota exceeeded for %s, tried to allocate '
+ 'address'),
+ context.project_id)
+ raise quota.QuotaError(_('Address quota exceeded. You cannot '
+ 'allocate any more addresses'))
# NOTE(vish): We don't know which network host should get the ip
# when we allocate, so just send it to any one. This
# will probably need to move into a network supervisor
# at some point.
return rpc.call(context,
FLAGS.network_topic,
- {"method": "allocate_floating_ip",
- "args": {"project_id": context.project_id}})
+ {'method': 'allocate_floating_ip',
+ 'args': {'project_id': context.project_id}})
def release_floating_ip(self, context, address,
affect_auto_assigned=False):
@@ -62,8 +61,8 @@ class API(base.Base):
# at some point.
rpc.cast(context,
FLAGS.network_topic,
- {"method": "deallocate_floating_ip",
- "args": {"floating_address": floating_ip['address']}})
+ {'method': 'deallocate_floating_ip',
+ 'args': {'floating_address': floating_ip['address']}})
def associate_floating_ip(self, context, floating_ip, fixed_ip,
affect_auto_assigned=False):
@@ -74,17 +73,17 @@ class API(base.Base):
return
# Check if the floating ip address is allocated
if floating_ip['project_id'] is None:
- raise exception.ApiError(_("Address (%s) is not allocated") %
+ raise exception.ApiError(_('Address (%s) is not allocated') %
floating_ip['address'])
# Check if the floating ip address is allocated to the same project
if floating_ip['project_id'] != context.project_id:
- LOG.warn(_("Address (%(address)s) is not allocated to your "
- "project (%(project)s)"),
+ LOG.warn(_('Address (%(address)s) is not allocated to your '
+ 'project (%(project)s)'),
{'address': floating_ip['address'],
'project': context.project_id})
- raise exception.ApiError(_("Address (%(address)s) is not "
- "allocated to your project"
- "(%(project)s)") %
+ raise exception.ApiError(_('Address (%(address)s) is not '
+ 'allocated to your project'
+ '(%(project)s)') %
{'address': floating_ip['address'],
'project': context.project_id})
# NOTE(vish): Perhaps we should just pass this on to compute and
@@ -92,9 +91,9 @@ class API(base.Base):
host = fixed_ip['network']['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.network_topic, host),
- {"method": "associate_floating_ip",
- "args": {"floating_address": floating_ip['address'],
- "fixed_address": fixed_ip['address']}})
+ {'method': 'associate_floating_ip',
+ 'args': {'floating_address': floating_ip['address'],
+ 'fixed_address': fixed_ip['address']}})
def disassociate_floating_ip(self, context, address,
affect_auto_assigned=False):
@@ -108,5 +107,5 @@ class API(base.Base):
host = floating_ip['fixed_ip']['network']['host']
rpc.cast(context,
self.db.queue_get_for(context, FLAGS.network_topic, host),
- {"method": "disassociate_floating_ip",
- "args": {"floating_address": floating_ip['address']}})
+ {'method': 'disassociate_floating_ip',
+ 'args': {'floating_address': floating_ip['address']}})
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index b50a4b4ea..815cd29c3 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -15,26 +15,27 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Implements vlans, bridges, and iptables rules using linux utilities.
-"""
+"""Implements vlans, bridges, and iptables rules using linux utilities."""
+
+import calendar
import inspect
import os
-import calendar
from nova import db
from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
+from IPy import IP
+
LOG = logging.getLogger("nova.linux_net")
def _bin_file(script):
- """Return the absolute path to scipt in the bin directory"""
- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
+ """Return the absolute path to scipt in the bin directory."""
+ return os.path.abspath(os.path.join(__file__, '../../../bin', script))
FLAGS = flags.FLAGS
@@ -66,11 +67,13 @@ binary_name = os.path.basename(inspect.stack()[-1][1])
class IptablesRule(object):
- """An iptables rule
+ """An iptables rule.
You shouldn't need to use this class directly, it's only used by
- IptablesManager
+ IptablesManager.
+
"""
+
def __init__(self, chain, rule, wrap=True, top=False):
self.chain = chain
self.rule = rule
@@ -95,7 +98,7 @@ class IptablesRule(object):
class IptablesTable(object):
- """An iptables table"""
+ """An iptables table."""
def __init__(self):
self.rules = []
@@ -103,15 +106,16 @@ class IptablesTable(object):
self.unwrapped_chains = set()
def add_chain(self, name, wrap=True):
- """Adds a named chain to the table
+ """Adds a named chain to the table.
The chain name is wrapped to be unique for the component creating
it, so different components of Nova can safely create identically
named chains without interfering with one another.
At the moment, its wrapped name is <binary name>-<chain name>,
- so if nova-compute creates a chain named "OUTPUT", it'll actually
- end up named "nova-compute-OUTPUT".
+ so if nova-compute creates a chain named 'OUTPUT', it'll actually
+ end up named 'nova-compute-OUTPUT'.
+
"""
if wrap:
self.chains.add(name)
@@ -119,12 +123,13 @@ class IptablesTable(object):
self.unwrapped_chains.add(name)
def remove_chain(self, name, wrap=True):
- """Remove named chain
+ """Remove named chain.
This removal "cascades". All rule in the chain are removed, as are
all rules in other chains that jump to it.
If the chain is not found, this is merely logged.
+
"""
if wrap:
chain_set = self.chains
@@ -132,7 +137,7 @@ class IptablesTable(object):
chain_set = self.unwrapped_chains
if name not in chain_set:
- LOG.debug(_("Attempted to remove chain %s which doesn't exist"),
+ LOG.debug(_('Attempted to remove chain %s which does not exist'),
name)
return
@@ -147,17 +152,18 @@ class IptablesTable(object):
self.rules = filter(lambda r: jump_snippet not in r.rule, self.rules)
def add_rule(self, chain, rule, wrap=True, top=False):
- """Add a rule to the table
+ """Add a rule to the table.
This is just like what you'd feed to iptables, just without
- the "-A <chain name>" bit at the start.
+ the '-A <chain name>' bit at the start.
However, if you need to jump to one of your wrapped chains,
prepend its name with a '$' which will ensure the wrapping
is applied correctly.
+
"""
if wrap and chain not in self.chains:
- raise ValueError(_("Unknown chain: %r") % chain)
+ raise ValueError(_('Unknown chain: %r') % chain)
if '$' in rule:
rule = ' '.join(map(self._wrap_target_chain, rule.split(' ')))
@@ -170,23 +176,24 @@ class IptablesTable(object):
return s
def remove_rule(self, chain, rule, wrap=True, top=False):
- """Remove a rule from a chain
+ """Remove a rule from a chain.
Note: The rule must be exactly identical to the one that was added.
You cannot switch arguments around like you can with the iptables
CLI tool.
+
"""
try:
self.rules.remove(IptablesRule(chain, rule, wrap, top))
except ValueError:
- LOG.debug(_("Tried to remove rule that wasn't there:"
- " %(chain)r %(rule)r %(wrap)r %(top)r"),
+ LOG.debug(_('Tried to remove rule that was not there:'
+ ' %(chain)r %(rule)r %(wrap)r %(top)r'),
{'chain': chain, 'rule': rule,
'top': top, 'wrap': wrap})
class IptablesManager(object):
- """Wrapper for iptables
+ """Wrapper for iptables.
See IptablesTable for some usage docs
@@ -205,7 +212,9 @@ class IptablesManager(object):
For ipv4, the builtin PREROUTING, OUTPUT, and POSTROUTING nat chains are
wrapped in the same was as the builtin filter chains. Additionally, there's
a snat chain that is applied after the POSTROUTING chain.
+
"""
+
def __init__(self, execute=None):
if not execute:
self.execute = _execute
@@ -267,11 +276,12 @@ class IptablesManager(object):
@utils.synchronized('iptables', external=True)
def apply(self):
- """Apply the current in-memory set of iptables rules
+ """Apply the current in-memory set of iptables rules.
This will blow away any rules left over from previous runs of the
same component of Nova, and replace them with our current set of
rules. This happens atomically, thanks to iptables-restore.
+
"""
s = [('iptables', self.ipv4)]
if FLAGS.use_ipv6:
@@ -348,63 +358,63 @@ class IptablesManager(object):
def metadata_forward():
- """Create forwarding rule for metadata"""
- iptables_manager.ipv4['nat'].add_rule("PREROUTING",
- "-s 0.0.0.0/0 -d 169.254.169.254/32 "
- "-p tcp -m tcp --dport 80 -j DNAT "
- "--to-destination %s:%s" % \
+ """Create forwarding rule for metadata."""
+ iptables_manager.ipv4['nat'].add_rule('PREROUTING',
+ '-s 0.0.0.0/0 -d 169.254.169.254/32 '
+ '-p tcp -m tcp --dport 80 -j DNAT '
+ '--to-destination %s:%s' % \
(FLAGS.ec2_dmz_host, FLAGS.ec2_port))
iptables_manager.apply()
def init_host():
- """Basic networking setup goes here"""
+ """Basic networking setup goes here."""
# NOTE(devcamcar): Cloud public SNAT entries and the default
# SNAT rule for outbound traffic.
- iptables_manager.ipv4['nat'].add_rule("snat",
- "-s %s -j SNAT --to-source %s" % \
+ iptables_manager.ipv4['nat'].add_rule('snat',
+ '-s %s -j SNAT --to-source %s' % \
(FLAGS.fixed_range,
FLAGS.routing_source_ip))
- iptables_manager.ipv4['nat'].add_rule("POSTROUTING",
- "-s %s -d %s -j ACCEPT" % \
+ iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
+ '-s %s -d %s -j ACCEPT' % \
(FLAGS.fixed_range, FLAGS.dmz_cidr))
- iptables_manager.ipv4['nat'].add_rule("POSTROUTING",
- "-s %(range)s -d %(range)s "
- "-j ACCEPT" % \
+ iptables_manager.ipv4['nat'].add_rule('POSTROUTING',
+ '-s %(range)s -d %(range)s '
+ '-j ACCEPT' % \
{'range': FLAGS.fixed_range})
iptables_manager.apply()
def bind_floating_ip(floating_ip, check_exit_code=True):
- """Bind ip to public interface"""
+ """Bind ip to public interface."""
_execute('sudo', 'ip', 'addr', 'add', floating_ip,
'dev', FLAGS.public_interface,
check_exit_code=check_exit_code)
def unbind_floating_ip(floating_ip):
- """Unbind a public ip from public interface"""
+ """Unbind a public ip from public interface."""
_execute('sudo', 'ip', 'addr', 'del', floating_ip,
'dev', FLAGS.public_interface)
def ensure_metadata_ip():
- """Sets up local metadata ip"""
+ """Sets up local metadata ip."""
_execute('sudo', 'ip', 'addr', 'add', '169.254.169.254/32',
'scope', 'link', 'dev', 'lo', check_exit_code=False)
def ensure_vlan_forward(public_ip, port, private_ip):
- """Sets up forwarding rules for vlan"""
- iptables_manager.ipv4['filter'].add_rule("FORWARD",
- "-d %s -p udp "
- "--dport 1194 "
- "-j ACCEPT" % private_ip)
- iptables_manager.ipv4['nat'].add_rule("PREROUTING",
- "-d %s -p udp "
- "--dport %s -j DNAT --to %s:1194" %
+ """Sets up forwarding rules for vlan."""
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '-d %s -p udp '
+ '--dport 1194 '
+ '-j ACCEPT' % private_ip)
+ iptables_manager.ipv4['nat'].add_rule('PREROUTING',
+ '-d %s -p udp '
+ '--dport %s -j DNAT --to %s:1194' %
(public_ip, port, private_ip))
iptables_manager.ipv4['nat'].add_rule("OUTPUT",
"-d %s -p udp "
@@ -414,37 +424,38 @@ def ensure_vlan_forward(public_ip, port, private_ip):
def ensure_floating_forward(floating_ip, fixed_ip):
- """Ensure floating ip forwarding rule"""
+ """Ensure floating ip forwarding rule."""
for chain, rule in floating_forward_rules(floating_ip, fixed_ip):
iptables_manager.ipv4['nat'].add_rule(chain, rule)
iptables_manager.apply()
def remove_floating_forward(floating_ip, fixed_ip):
- """Remove forwarding for floating ip"""
+ """Remove forwarding for floating ip."""
for chain, rule in floating_forward_rules(floating_ip, fixed_ip):
iptables_manager.ipv4['nat'].remove_rule(chain, rule)
iptables_manager.apply()
def floating_forward_rules(floating_ip, fixed_ip):
- return [("PREROUTING", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)),
- ("OUTPUT", "-d %s -j DNAT --to %s" % (floating_ip, fixed_ip)),
- ("floating-snat",
- "-s %s -j SNAT --to %s" % (fixed_ip, floating_ip))]
+ return [('PREROUTING', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)),
+ ('OUTPUT', '-d %s -j DNAT --to %s' % (floating_ip, fixed_ip)),
+ ('floating-snat',
+ '-s %s -j SNAT --to %s' % (fixed_ip, floating_ip))]
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
- """Create a vlan and bridge unless they already exist"""
+ """Create a vlan and bridge unless they already exist."""
interface = ensure_vlan(vlan_num)
ensure_bridge(bridge, interface, net_attrs)
+@utils.synchronized('ensure_vlan', external=True)
def ensure_vlan(vlan_num):
- """Create a vlan unless it already exists"""
- interface = "vlan%s" % vlan_num
+ """Create a vlan unless it already exists."""
+ interface = 'vlan%s' % vlan_num
if not _device_exists(interface):
- LOG.debug(_("Starting VLAN inteface %s"), interface)
+ LOG.debug(_('Starting VLAN inteface %s'), interface)
_execute('sudo', 'vconfig', 'set_name_type', 'VLAN_PLUS_VID_NO_PAD')
_execute('sudo', 'vconfig', 'add', FLAGS.vlan_interface, vlan_num)
_execute('sudo', 'ip', 'link', 'set', interface, 'up')
@@ -464,12 +475,13 @@ def ensure_bridge(bridge, interface, net_attrs=None):
The code will attempt to move any ips that already exist on the interface
onto the bridge and reset the default gateway if necessary.
+
"""
if not _device_exists(bridge):
- LOG.debug(_("Starting Bridge interface for %s"), interface)
+ LOG.debug(_('Starting Bridge interface for %s'), interface)
_execute('sudo', 'brctl', 'addbr', bridge)
_execute('sudo', 'brctl', 'setfd', bridge, 0)
- # _execute("sudo brctl setageing %s 10" % bridge)
+ # _execute('sudo brctl setageing %s 10' % bridge)
_execute('sudo', 'brctl', 'stp', bridge, 'off')
_execute('sudo', 'ip', 'link', 'set', bridge, 'up')
if net_attrs:
@@ -477,15 +489,15 @@ def ensure_bridge(bridge, interface, net_attrs=None):
# bridge for it to respond to reqests properly
suffix = net_attrs['cidr'].rpartition('/')[2]
out, err = _execute('sudo', 'ip', 'addr', 'add',
- "%s/%s" %
+ '%s/%s' %
(net_attrs['gateway'], suffix),
'brd',
net_attrs['broadcast'],
'dev',
bridge,
check_exit_code=False)
- if err and err != "RTNETLINK answers: File exists\n":
- raise exception.Error("Failed to add ip: %s" % err)
+ if err and err != 'RTNETLINK answers: File exists\n':
+ raise exception.Error('Failed to add ip: %s' % err)
if(FLAGS.use_ipv6):
_execute('sudo', 'ip', '-f', 'inet6', 'addr',
'change', net_attrs['cidr_v6'],
@@ -501,17 +513,17 @@ def ensure_bridge(bridge, interface, net_attrs=None):
# interface, so we move any ips to the bridge
gateway = None
out, err = _execute('sudo', 'route', '-n')
- for line in out.split("\n"):
+ for line in out.split('\n'):
fields = line.split()
- if fields and fields[0] == "0.0.0.0" and fields[-1] == interface:
+ if fields and fields[0] == '0.0.0.0' and fields[-1] == interface:
gateway = fields[1]
_execute('sudo', 'route', 'del', 'default', 'gw', gateway,
'dev', interface, check_exit_code=False)
out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface,
'scope', 'global')
- for line in out.split("\n"):
+ for line in out.split('\n'):
fields = line.split()
- if fields and fields[0] == "inet":
+ if fields and fields[0] == 'inet':
params = fields[1:-1]
_execute(*_ip_bridge_cmd('del', params, fields[-1]))
_execute(*_ip_bridge_cmd('add', params, bridge))
@@ -522,18 +534,18 @@ def ensure_bridge(bridge, interface, net_attrs=None):
if (err and err != "device %s is already a member of a bridge; can't "
"enslave it to bridge %s.\n" % (interface, bridge)):
- raise exception.Error("Failed to add interface: %s" % err)
+ raise exception.Error('Failed to add interface: %s' % err)
- iptables_manager.ipv4['filter'].add_rule("FORWARD",
- "--in-interface %s -j ACCEPT" % \
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--in-interface %s -j ACCEPT' % \
bridge)
- iptables_manager.ipv4['filter'].add_rule("FORWARD",
- "--out-interface %s -j ACCEPT" % \
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--out-interface %s -j ACCEPT' % \
bridge)
def get_dhcp_leases(context, network_id):
- """Return a network's hosts config in dnsmasq leasefile format"""
+ """Return a network's hosts config in dnsmasq leasefile format."""
hosts = []
for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
network_id):
@@ -542,7 +554,7 @@ def get_dhcp_leases(context, network_id):
def get_dhcp_hosts(context, network_id):
- """Get a string containing a network's hosts config in dhcp-host format"""
+ """Get network's hosts config in dhcp-host format."""
hosts = []
for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
network_id):
@@ -555,10 +567,11 @@ def get_dhcp_hosts(context, network_id):
# aren't reloaded.
@utils.synchronized('dnsmasq_start')
def update_dhcp(context, network_id):
- """(Re)starts a dnsmasq server for a given network
+ """(Re)starts a dnsmasq server for a given network.
+
+ If a dnsmasq instance is already running then send a HUP
+ signal causing it to reload, otherwise spawn a new instance.
- if a dnsmasq instance is already running then send a HUP
- signal causing it to reload, otherwise spawn a new instance
"""
network_ref = db.network_get(context, network_id)
@@ -573,16 +586,16 @@ def update_dhcp(context, network_id):
# if dnsmasq is already running, then tell it to reload
if pid:
- out, _err = _execute('cat', "/proc/%d/cmdline" % pid,
+ out, _err = _execute('cat', '/proc/%d/cmdline' % pid,
check_exit_code=False)
if conffile in out:
try:
_execute('sudo', 'kill', '-HUP', pid)
return
except Exception as exc: # pylint: disable=W0703
- LOG.debug(_("Hupping dnsmasq threw %s"), exc)
+ LOG.debug(_('Hupping dnsmasq threw %s'), exc)
else:
- LOG.debug(_("Pid %d is stale, relaunching dnsmasq"), pid)
+ LOG.debug(_('Pid %d is stale, relaunching dnsmasq'), pid)
# FLAGFILE and DNSMASQ_INTERFACE in env
env = {'FLAGFILE': FLAGS.dhcpbridge_flagfile,
@@ -625,18 +638,18 @@ interface %s
try:
_execute('sudo', 'kill', pid)
except Exception as exc: # pylint: disable=W0703
- LOG.debug(_("killing radvd threw %s"), exc)
+ LOG.debug(_('killing radvd threw %s'), exc)
else:
- LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
+ LOG.debug(_('Pid %d is stale, relaunching radvd'), pid)
command = _ra_cmd(network_ref)
_execute(*command)
db.network_update(context, network_id,
- {"gateway_v6":
+ {'gateway_v6':
utils.get_my_linklocal(network_ref['bridge'])})
def _host_lease(fixed_ip_ref):
- """Return a host string for an address in leasefile format"""
+ """Return a host string for an address in leasefile format."""
instance_ref = fixed_ip_ref['instance']
if instance_ref['updated_at']:
timestamp = instance_ref['updated_at']
@@ -645,39 +658,39 @@ def _host_lease(fixed_ip_ref):
seconds_since_epoch = calendar.timegm(timestamp.utctimetuple())
- return "%d %s %s %s *" % (seconds_since_epoch + FLAGS.dhcp_lease_time,
+ return '%d %s %s %s *' % (seconds_since_epoch + FLAGS.dhcp_lease_time,
instance_ref['mac_address'],
fixed_ip_ref['address'],
instance_ref['hostname'] or '*')
def _host_dhcp(fixed_ip_ref):
- """Return a host string for an address in dhcp-host format"""
+ """Return a host string for an address in dhcp-host format."""
instance_ref = fixed_ip_ref['instance']
- return "%s,%s.%s,%s" % (instance_ref['mac_address'],
+ return '%s,%s.%s,%s' % (instance_ref['mac_address'],
instance_ref['hostname'],
FLAGS.dhcp_domain,
fixed_ip_ref['address'])
def _execute(*cmd, **kwargs):
- """Wrapper around utils._execute for fake_network"""
+ """Wrapper around utils._execute for fake_network."""
if FLAGS.fake_network:
- LOG.debug("FAKE NET: %s", " ".join(map(str, cmd)))
- return "fake", 0
+ LOG.debug('FAKE NET: %s', ' '.join(map(str, cmd)))
+ return 'fake', 0
else:
return utils.execute(*cmd, **kwargs)
def _device_exists(device):
- """Check if ethernet device exists"""
+ """Check if ethernet device exists."""
(_out, err) = _execute('ip', 'link', 'show', 'dev', device,
check_exit_code=False)
return not err
def _dnsmasq_cmd(net):
- """Builds dnsmasq command"""
+ """Builds dnsmasq command."""
cmd = ['sudo', '-E', 'dnsmasq',
'--strict-order',
'--bind-interfaces',
@@ -687,6 +700,7 @@ def _dnsmasq_cmd(net):
'--listen-address=%s' % net['gateway'],
'--except-interface=lo',
'--dhcp-range=%s,static,120s' % net['dhcp_start'],
+ '--dhcp-lease-max=%s' % IP(net['cidr']).len(),
'--dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'),
'--dhcp-script=%s' % FLAGS.dhcpbridge,
'--leasefile-ro']
@@ -696,7 +710,7 @@ def _dnsmasq_cmd(net):
def _ra_cmd(net):
- """Builds radvd command"""
+ """Builds radvd command."""
cmd = ['sudo', '-E', 'radvd',
# '-u', 'nobody',
'-C', '%s' % _ra_file(net['bridge'], 'conf'),
@@ -705,44 +719,43 @@ def _ra_cmd(net):
def _stop_dnsmasq(network):
- """Stops the dnsmasq instance for a given network"""
+ """Stops the dnsmasq instance for a given network."""
pid = _dnsmasq_pid_for(network)
if pid:
try:
_execute('sudo', 'kill', '-TERM', pid)
except Exception as exc: # pylint: disable=W0703
- LOG.debug(_("Killing dnsmasq threw %s"), exc)
+ LOG.debug(_('Killing dnsmasq threw %s'), exc)
def _dhcp_file(bridge, kind):
- """Return path to a pid, leases or conf file for a bridge"""
-
+ """Return path to a pid, leases or conf file for a bridge."""
if not os.path.exists(FLAGS.networks_path):
os.makedirs(FLAGS.networks_path)
- return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path,
+ return os.path.abspath('%s/nova-%s.%s' % (FLAGS.networks_path,
bridge,
kind))
def _ra_file(bridge, kind):
- """Return path to a pid or conf file for a bridge"""
+ """Return path to a pid or conf file for a bridge."""
if not os.path.exists(FLAGS.networks_path):
os.makedirs(FLAGS.networks_path)
- return os.path.abspath("%s/nova-ra-%s.%s" % (FLAGS.networks_path,
+ return os.path.abspath('%s/nova-ra-%s.%s' % (FLAGS.networks_path,
bridge,
kind))
def _dnsmasq_pid_for(bridge):
- """Returns the pid for prior dnsmasq instance for a bridge
+ """Returns the pid for prior dnsmasq instance for a bridge.
- Returns None if no pid file exists
+ Returns None if no pid file exists.
- If machine has rebooted pid might be incorrect (caller should check)
- """
+ If machine has rebooted pid might be incorrect (caller should check).
+ """
pid_file = _dhcp_file(bridge, 'pid')
if os.path.exists(pid_file):
@@ -751,13 +764,13 @@ def _dnsmasq_pid_for(bridge):
def _ra_pid_for(bridge):
- """Returns the pid for prior radvd instance for a bridge
+ """Returns the pid for prior radvd instance for a bridge.
- Returns None if no pid file exists
+ Returns None if no pid file exists.
- If machine has rebooted pid might be incorrect (caller should check)
- """
+ If machine has rebooted pid might be incorrect (caller should check).
+ """
pid_file = _ra_file(bridge, 'pid')
if os.path.exists(pid_file):
@@ -766,8 +779,7 @@ def _ra_pid_for(bridge):
def _ip_bridge_cmd(action, params, device):
- """Build commands to add/del ips to bridges/devices"""
-
+ """Build commands to add/del ips to bridges/devices."""
cmd = ['sudo', 'ip', 'addr', action]
cmd.extend(params)
cmd.extend(['dev', device])
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 0dd7f2360..5a6fdde5a 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -16,8 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Network Hosts are responsible for allocating ips and setting up network.
+"""Network Hosts are responsible for allocating ips and setting up network.
There are multiple backend drivers that handle specific types of networking
topologies. All of the network commands are issued to a subclass of
@@ -61,6 +60,8 @@ from nova import rpc
LOG = logging.getLogger("nova.network.manager")
+
+
FLAGS = flags.FLAGS
flags.DEFINE_string('flat_network_bridge', 'br100',
'Bridge for simple network instances')
@@ -111,7 +112,9 @@ class NetworkManager(manager.SchedulerDependentManager):
"""Implements common network manager functionality.
This class must be subclassed to support specific topologies.
+
"""
+
timeout_fixed_ips = True
def __init__(self, network_driver=None, *args, **kwargs):
@@ -122,9 +125,7 @@ class NetworkManager(manager.SchedulerDependentManager):
*args, **kwargs)
def init_host(self):
- """Do any initialization that needs to be run if this is a
- standalone service.
- """
+ """Do any initialization for a standalone service."""
self.driver.init_host()
self.driver.ensure_metadata_ip()
# Set up networking for the projects for which we're already
@@ -154,11 +155,11 @@ class NetworkManager(manager.SchedulerDependentManager):
self.host,
time)
if num:
- LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num)
+ LOG.debug(_('Dissassociated %s stale fixed ip(s)'), num)
def set_network_host(self, context, network_id):
"""Safely sets the host of the network."""
- LOG.debug(_("setting network host"), context=context)
+ LOG.debug(_('setting network host'), context=context)
host = self.db.network_set_host(context,
network_id,
self.host)
@@ -224,39 +225,39 @@ class NetworkManager(manager.SchedulerDependentManager):
def lease_fixed_ip(self, context, mac, address):
"""Called by dhcp-bridge when ip is leased."""
- LOG.debug(_("Leasing IP %s"), address, context=context)
+ LOG.debug(_('Leasing IP %s'), address, context=context)
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
instance_ref = fixed_ip_ref['instance']
if not instance_ref:
- raise exception.Error(_("IP %s leased that isn't associated") %
+ raise exception.Error(_('IP %s leased that is not associated') %
address)
if instance_ref['mac_address'] != mac:
inst_addr = instance_ref['mac_address']
- raise exception.Error(_("IP %(address)s leased to bad"
- " mac %(inst_addr)s vs %(mac)s") % locals())
+ raise exception.Error(_('IP %(address)s leased to bad mac'
+ ' %(inst_addr)s vs %(mac)s') % locals())
now = datetime.datetime.utcnow()
self.db.fixed_ip_update(context,
fixed_ip_ref['address'],
{'leased': True,
'updated_at': now})
if not fixed_ip_ref['allocated']:
- LOG.warn(_("IP %s leased that was already deallocated"), address,
+ LOG.warn(_('IP %s leased that was already deallocated'), address,
context=context)
def release_fixed_ip(self, context, mac, address):
"""Called by dhcp-bridge when ip is released."""
- LOG.debug(_("Releasing IP %s"), address, context=context)
+ LOG.debug(_('Releasing IP %s'), address, context=context)
fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
instance_ref = fixed_ip_ref['instance']
if not instance_ref:
- raise exception.Error(_("IP %s released that isn't associated") %
+ raise exception.Error(_('IP %s released that is not associated') %
address)
if instance_ref['mac_address'] != mac:
inst_addr = instance_ref['mac_address']
- raise exception.Error(_("IP %(address)s released from"
- " bad mac %(inst_addr)s vs %(mac)s") % locals())
+ raise exception.Error(_('IP %(address)s released from bad mac'
+ ' %(inst_addr)s vs %(mac)s') % locals())
if not fixed_ip_ref['leased']:
- LOG.warn(_("IP %s released that was not leased"), address,
+ LOG.warn(_('IP %s released that was not leased'), address,
context=context)
self.db.fixed_ip_update(context,
fixed_ip_ref['address'],
@@ -286,8 +287,8 @@ class NetworkManager(manager.SchedulerDependentManager):
return self.set_network_host(context, network_ref['id'])
host = rpc.call(context,
FLAGS.network_topic,
- {"method": "set_network_host",
- "args": {"network_id": network_ref['id']}})
+ {'method': 'set_network_host',
+ 'args': {'network_id': network_ref['id']}})
return host
def create_networks(self, context, cidr, num_networks, network_size,
@@ -302,7 +303,7 @@ class NetworkManager(manager.SchedulerDependentManager):
start = index * network_size
start_v6 = index * network_size_v6
significant_bits = 32 - int(math.log(network_size, 2))
- cidr = "%s/%s" % (fixed_net[start], significant_bits)
+ cidr = '%s/%s' % (fixed_net[start], significant_bits)
project_net = IPy.IP(cidr)
net = {}
net['bridge'] = FLAGS.flat_network_bridge
@@ -313,13 +314,13 @@ class NetworkManager(manager.SchedulerDependentManager):
net['broadcast'] = str(project_net.broadcast())
net['dhcp_start'] = str(project_net[2])
if num_networks > 1:
- net['label'] = "%s_%d" % (label, count)
+ net['label'] = '%s_%d' % (label, count)
else:
net['label'] = label
count += 1
if(FLAGS.use_ipv6):
- cidr_v6 = "%s/%s" % (fixed_net_v6[start_v6],
+ cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6],
significant_bits_v6)
net['cidr_v6'] = cidr_v6
project_net_v6 = IPy.IP(cidr_v6)
@@ -386,13 +387,13 @@ class FlatManager(NetworkManager):
Metadata forwarding must be handled by the gateway, and since nova does
not do any setup in this mode, it must be done manually. Requests to
169.254.169.254 port 80 will need to be forwarded to the api server.
+
"""
+
timeout_fixed_ips = False
def init_host(self):
- """Do any initialization that needs to be run if this is a
- standalone service.
- """
+ """Do any initialization for a standalone service."""
#Fix for bug 723298 - do not call init_host on superclass
#Following code has been copied for NetworkManager.init_host
ctxt = context.get_admin_context()
@@ -433,12 +434,11 @@ class FlatDHCPManager(NetworkManager):
FlatDHCPManager will start up one dhcp server to give out addresses.
It never injects network settings into the guest. Otherwise it behaves
like FlatDHCPManager.
+
"""
def init_host(self):
- """Do any initialization that needs to be run if this is a
- standalone service.
- """
+ """Do any initialization for a standalone service."""
super(FlatDHCPManager, self).init_host()
self.driver.metadata_forward()
@@ -490,12 +490,11 @@ class VlanManager(NetworkManager):
A dhcp server is run for each subnet, so each project will have its own.
For this mode to be useful, each project will need a vpn to access the
instances in its subnet.
+
"""
def init_host(self):
- """Do any initialization that needs to be run if this is a
- standalone service.
- """
+ """Do any initialization for a standalone service."""
super(VlanManager, self).init_host()
self.driver.metadata_forward()
@@ -566,7 +565,7 @@ class VlanManager(NetworkManager):
net['vlan'] = vlan
net['bridge'] = 'br%s' % vlan
if(FLAGS.use_ipv6):
- cidr_v6 = "%s/%s" % (fixed_net_v6[start_v6],
+ cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6],
significant_bits_v6)
net['cidr_v6'] = cidr_v6
@@ -600,8 +599,8 @@ class VlanManager(NetworkManager):
return self.set_network_host(context, network_ref['id'])
host = rpc.call(context,
FLAGS.network_topic,
- {"method": "set_network_host",
- "args": {"network_id": network_ref['id']}})
+ {'method': 'set_network_host',
+ 'args': {'network_id': network_ref['id']}})
return host
diff --git a/nova/network/vmwareapi_net.py b/nova/network/vmwareapi_net.py
index 9b2db7b8f..373060add 100644
--- a/nova/network/vmwareapi_net.py
+++ b/nova/network/vmwareapi_net.py
@@ -15,9 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Implements vlans for vmwareapi.
-"""
+"""Implements vlans for vmwareapi."""
from nova import db
from nova import exception
@@ -27,8 +25,10 @@ from nova import utils
from nova.virt.vmwareapi_conn import VMWareAPISession
from nova.virt.vmwareapi import network_utils
+
LOG = logging.getLogger("nova.network.vmwareapi_net")
+
FLAGS = flags.FLAGS
flags.DEFINE_string('vlan_interface', 'vmnic0',
'Physical network adapter name in VMware ESX host for '
@@ -42,10 +42,10 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
host_username = FLAGS.vmwareapi_host_username
host_password = FLAGS.vmwareapi_host_password
if not host_ip or host_username is None or host_password is None:
- raise Exception(_("Must specify vmwareapi_host_ip,"
- "vmwareapi_host_username "
- "and vmwareapi_host_password to use"
- "connection_type=vmwareapi"))
+ raise Exception(_('Must specify vmwareapi_host_ip, '
+ 'vmwareapi_host_username '
+ 'and vmwareapi_host_password to use '
+ 'connection_type=vmwareapi'))
session = VMWareAPISession(host_ip, host_username, host_password,
FLAGS.vmwareapi_api_retry_count)
vlan_interface = FLAGS.vlan_interface
diff --git a/nova/network/xenapi_net.py b/nova/network/xenapi_net.py
index 8c22a7d4b..709ef7f34 100644
--- a/nova/network/xenapi_net.py
+++ b/nova/network/xenapi_net.py
@@ -15,9 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""
-Implements vlans, bridges, and iptables rules using linux utilities.
-"""
+"""Implements vlans, bridges, and iptables rules using linux utilities."""
import os
@@ -26,22 +24,24 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
-from nova.virt.xenapi_conn import XenAPISession
+from nova.virt import xenapi_conn
from nova.virt.xenapi import network_utils
+
LOG = logging.getLogger("nova.xenapi_net")
+
FLAGS = flags.FLAGS
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
"""Create a vlan and bridge unless they already exist."""
# Open xenapi session
- LOG.debug("ENTERING ensure_vlan_bridge in xenapi net")
+ LOG.debug('ENTERING ensure_vlan_bridge in xenapi net')
url = FLAGS.xenapi_connection_url
username = FLAGS.xenapi_connection_username
password = FLAGS.xenapi_connection_password
- session = XenAPISession(url, username, password)
+ session = xenapi_conn.XenAPISession(url, username, password)
# Check whether bridge already exists
# Retrieve network whose name_label is "bridge"
network_ref = network_utils.NetworkHelper.find_network_with_name_label(
@@ -50,14 +50,14 @@ def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
if network_ref is None:
# If bridge does not exists
# 1 - create network
- description = "network for nova bridge %s" % bridge
+ description = 'network for nova bridge %s' % bridge
network_rec = {'name_label': bridge,
'name_description': description,
'other_config': {}}
network_ref = session.call_xenapi('network.create', network_rec)
# 2 - find PIF for VLAN
- expr = 'field "device" = "%s" and \
- field "VLAN" = "-1"' % FLAGS.vlan_interface
+ expr = "field 'device' = '%s' and \
+ field 'VLAN' = '-1'" % FLAGS.vlan_interface
pifs = session.call_xenapi('PIF.get_all_records_where', expr)
pif_ref = None
# Multiple PIF are ok: we are dealing with a pool
diff --git a/nova/notifier/__init__.py b/nova/notifier/__init__.py
new file mode 100644
index 000000000..482d54e4f
--- /dev/null
+++ b/nova/notifier/__init__.py
@@ -0,0 +1,14 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
diff --git a/nova/notifier/api.py b/nova/notifier/api.py
new file mode 100644
index 000000000..a3e7a039e
--- /dev/null
+++ b/nova/notifier/api.py
@@ -0,0 +1,83 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.import datetime
+
+import datetime
+import uuid
+
+from nova import flags
+from nova import utils
+
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('default_notification_level', 'INFO',
+ 'Default notification level for outgoing notifications')
+
+WARN = 'WARN'
+INFO = 'INFO'
+ERROR = 'ERROR'
+CRITICAL = 'CRITICAL'
+DEBUG = 'DEBUG'
+
+log_levels = (DEBUG, WARN, INFO, ERROR, CRITICAL)
+
+
+class BadPriorityException(Exception):
+ pass
+
+
+def notify(publisher_id, event_type, priority, payload):
+ """
+ Sends a notification using the specified driver
+
+ Notify parameters:
+
+ publisher_id - the source worker_type.host of the message
+ event_type - the literal type of event (ex. Instance Creation)
+ priority - patterned after the enumeration of Python logging levels in
+ the set (DEBUG, WARN, INFO, ERROR, CRITICAL)
+ payload - A python dictionary of attributes
+
+ Outgoing message format includes the above parameters, and appends the
+ following:
+
+ message_id - a UUID representing the id for this notification
+ timestamp - the GMT timestamp the notification was sent at
+
+ The composite message will be constructed as a dictionary of the above
+ attributes, which will then be sent via the transport mechanism defined
+ by the driver.
+
+ Message example:
+
+ {'message_id': str(uuid.uuid4()),
+ 'publisher_id': 'compute.host1',
+ 'timestamp': datetime.datetime.utcnow(),
+ 'priority': 'WARN',
+ 'event_type': 'compute.create_instance',
+ 'payload': {'instance_id': 12, ... }}
+
+ """
+ if priority not in log_levels:
+ raise BadPriorityException(
+ _('%s not in valid priorities' % priority))
+ driver = utils.import_object(FLAGS.notification_driver)
+ msg = dict(message_id=str(uuid.uuid4()),
+ publisher_id=publisher_id,
+ event_type=event_type,
+ priority=priority,
+ payload=payload,
+ timestamp=str(datetime.datetime.utcnow()))
+ driver.notify(msg)
diff --git a/nova/notifier/log_notifier.py b/nova/notifier/log_notifier.py
new file mode 100644
index 000000000..25dfc693b
--- /dev/null
+++ b/nova/notifier/log_notifier.py
@@ -0,0 +1,34 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from nova import flags
+from nova import log as logging
+
+
+FLAGS = flags.FLAGS
+
+
+def notify(message):
+ """Notifies the recipient of the desired event given the model.
+ Log notifications using nova's default logging system"""
+
+ priority = message.get('priority',
+ FLAGS.default_notification_level)
+ priority = priority.lower()
+ logger = logging.getLogger(
+ 'nova.notification.%s' % message['event_type'])
+ getattr(logger, priority)(json.dumps(message))
diff --git a/nova/notifier/no_op_notifier.py b/nova/notifier/no_op_notifier.py
new file mode 100644
index 000000000..029710505
--- /dev/null
+++ b/nova/notifier/no_op_notifier.py
@@ -0,0 +1,19 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+def notify(message):
+ """Notifies the recipient of the desired event given the model"""
+ pass
diff --git a/nova/notifier/rabbit_notifier.py b/nova/notifier/rabbit_notifier.py
new file mode 100644
index 000000000..d46670b58
--- /dev/null
+++ b/nova/notifier/rabbit_notifier.py
@@ -0,0 +1,36 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+import nova.context
+
+from nova import flags
+from nova import rpc
+
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('notification_topic', 'notifications',
+ 'RabbitMQ topic used for Nova notifications')
+
+
+def notify(message):
+ """Sends a notification to the RabbitMQ"""
+ context = nova.context.get_admin_context()
+ priority = message.get('priority',
+ FLAGS.default_notification_level)
+ priority = priority.lower()
+ topic = '%s.%s' % (FLAGS.notification_topic, priority)
+ rpc.cast(context, topic, message)
diff --git a/nova/service.py b/nova/service.py
index 2532b9df2..ab1238c3b 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -240,6 +240,10 @@ class WsgiService(object):
def wait(self):
self.wsgi_app.wait()
+ def get_socket_info(self, api_name):
+ """Returns the (host, port) that an API was started on."""
+ return self.wsgi_app.socket_info[api_name]
+
class ApiService(WsgiService):
"""Class for our nova-api service."""
@@ -318,8 +322,10 @@ def _run_wsgi(paste_config_file, apis):
logging.debug(_('App Config: %(api)s\n%(config)r') % locals())
logging.info(_('Running %s API'), api)
app = wsgi.load_paste_app(paste_config_file, api)
- apps.append((app, getattr(FLAGS, '%s_listen_port' % api),
- getattr(FLAGS, '%s_listen' % api)))
+ apps.append((app,
+ getattr(FLAGS, '%s_listen_port' % api),
+ getattr(FLAGS, '%s_listen' % api),
+ api))
if len(apps) == 0:
logging.error(_('No known API applications configured in %s.'),
paste_config_file)
diff --git a/nova/tests/api/openstack/extensions/foxinsocks.py b/nova/tests/api/openstack/extensions/foxinsocks.py
index 0860b51ac..dbdd0928a 100644
--- a/nova/tests/api/openstack/extensions/foxinsocks.py
+++ b/nova/tests/api/openstack/extensions/foxinsocks.py
@@ -63,31 +63,33 @@ class Foxinsocks(object):
self._delete_tweedle))
return actions
- def get_response_extensions(self):
- response_exts = []
+ def get_request_extensions(self):
+ request_exts = []
- def _goose_handler(res):
+ def _goose_handler(req, res):
#NOTE: This only handles JSON responses.
# You can use content type header to test for XML.
data = json.loads(res.body)
- data['flavor']['googoose'] = "Gooey goo for chewy chewing!"
- return data
+ data['flavor']['googoose'] = req.GET.get('chewing')
+ res.body = json.dumps(data)
+ return res
- resp_ext = extensions.ResponseExtension('GET', '/v1.1/flavors/:(id)',
+ req_ext1 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)',
_goose_handler)
- response_exts.append(resp_ext)
+ request_exts.append(req_ext1)
- def _bands_handler(res):
+ def _bands_handler(req, res):
#NOTE: This only handles JSON responses.
# You can use content type header to test for XML.
data = json.loads(res.body)
data['big_bands'] = 'Pig Bands!'
- return data
+ res.body = json.dumps(data)
+ return res
- resp_ext2 = extensions.ResponseExtension('GET', '/v1.1/flavors/:(id)',
+ req_ext2 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)',
_bands_handler)
- response_exts.append(resp_ext2)
- return response_exts
+ request_exts.append(req_ext2)
+ return request_exts
def _add_tweedle(self, input_dict, req, id):
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 8b0729c35..bf51239e6 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -228,6 +228,9 @@ class FakeToken(object):
# FIXME(sirp): let's not use id here
id = 0
+ def __getitem__(self, key):
+ return getattr(self, key)
+
def __init__(self, **kwargs):
FakeToken.id += 1
self.id = FakeToken.id
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 481d34ed1..544298602 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -45,10 +45,10 @@ class StubController(nova.wsgi.Controller):
class StubExtensionManager(object):
- def __init__(self, resource_ext=None, action_ext=None, response_ext=None):
+ def __init__(self, resource_ext=None, action_ext=None, request_ext=None):
self.resource_ext = resource_ext
self.action_ext = action_ext
- self.response_ext = response_ext
+ self.request_ext = request_ext
def get_name(self):
return "Tweedle Beetle Extension"
@@ -71,11 +71,11 @@ class StubExtensionManager(object):
action_exts.append(self.action_ext)
return action_exts
- def get_response_extensions(self):
- response_exts = []
- if self.response_ext:
- response_exts.append(self.response_ext)
- return response_exts
+ def get_request_extensions(self):
+ request_extensions = []
+ if self.request_ext:
+ request_extensions.append(self.request_ext)
+ return request_extensions
class ExtensionControllerTest(unittest.TestCase):
@@ -183,10 +183,10 @@ class ActionExtensionTest(unittest.TestCase):
self.assertEqual(404, response.status_int)
-class ResponseExtensionTest(unittest.TestCase):
+class RequestExtensionTest(unittest.TestCase):
def setUp(self):
- super(ResponseExtensionTest, self).setUp()
+ super(RequestExtensionTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
@@ -195,42 +195,39 @@ class ResponseExtensionTest(unittest.TestCase):
def tearDown(self):
self.stubs.UnsetAll()
- super(ResponseExtensionTest, self).tearDown()
+ super(RequestExtensionTest, self).tearDown()
def test_get_resources_with_stub_mgr(self):
- test_resp = "Gooey goo for chewy chewing!"
-
- def _resp_handler(res):
+ def _req_handler(req, res):
# only handle JSON responses
data = json.loads(res.body)
- data['flavor']['googoose'] = test_resp
- return data
+ data['flavor']['googoose'] = req.GET.get('chewing')
+ res.body = json.dumps(data)
+ return res
- resp_ext = extensions.ResponseExtension('GET',
+ req_ext = extensions.RequestExtension('GET',
'/v1.1/flavors/:(id)',
- _resp_handler)
+ _req_handler)
- manager = StubExtensionManager(None, None, resp_ext)
+ manager = StubExtensionManager(None, None, req_ext)
app = fakes.wsgi_app()
ext_midware = extensions.ExtensionMiddleware(app, manager)
- request = webob.Request.blank("/v1.1/flavors/1")
+ request = webob.Request.blank("/v1.1/flavors/1?chewing=bluegoo")
request.environ['api.version'] = '1.1'
response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int)
response_data = json.loads(response.body)
- self.assertEqual(test_resp, response_data['flavor']['googoose'])
+ self.assertEqual('bluegoo', response_data['flavor']['googoose'])
def test_get_resources_with_mgr(self):
- test_resp = "Gooey goo for chewy chewing!"
-
app = fakes.wsgi_app()
ext_midware = extensions.ExtensionMiddleware(app)
- request = webob.Request.blank("/v1.1/flavors/1")
+ request = webob.Request.blank("/v1.1/flavors/1?chewing=newblue")
request.environ['api.version'] = '1.1'
response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int)
response_data = json.loads(response.body)
- self.assertEqual(test_resp, response_data['flavor']['googoose'])
+ self.assertEqual('newblue', response_data['flavor']['googoose'])
self.assertEqual("Pig Bands!", response_data['big_bands'])
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index b42b3e7d8..fa2e05033 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -21,6 +21,7 @@ import json
import nova.db
from nova import context
from nova import crypto
+from nova import exception
from nova import flags
from nova import test
from nova.api.openstack import zones
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index 2e5d67017..bc98921f0 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -160,7 +160,7 @@ class _IntegratedTestBase(test.TestCase):
#self.start_service('network')
self.start_service('scheduler')
- self.auth_url = self._start_api_service()
+ self._start_api_service()
self.context = IntegratedUnitTestContext(self.auth_url)
@@ -174,8 +174,10 @@ class _IntegratedTestBase(test.TestCase):
if not api_service:
raise Exception("API Service was None")
- auth_url = 'http://localhost:8774/v1.1'
- return auth_url
+ self.api_service = api_service
+
+ host, port = api_service.get_socket_info('osapi')
+ self.auth_url = 'http://%s:%s/v1.1' % (host, port)
def tearDown(self):
self.context.cleanup()
@@ -184,6 +186,11 @@ class _IntegratedTestBase(test.TestCase):
def _get_flags(self):
"""An opportunity to setup flags, before the services are started."""
f = {}
+
+ # Auto-assign ports to allow concurrent tests
+ f['ec2_listen_port'] = 0
+ f['osapi_listen_port'] = 0
+
f['image_service'] = 'nova.image.fake.FakeImageService'
f['fake_network'] = True
return f
diff --git a/nova/tests/public_key/dummy.fingerprint b/nova/tests/public_key/dummy.fingerprint
new file mode 100644
index 000000000..715bca27a
--- /dev/null
+++ b/nova/tests/public_key/dummy.fingerprint
@@ -0,0 +1 @@
+1c:87:d1:d9:32:fd:62:3c:78:2b:c0:ad:c0:15:88:df
diff --git a/nova/tests/public_key/dummy.pub b/nova/tests/public_key/dummy.pub
new file mode 100644
index 000000000..d4cf2bc0d
--- /dev/null
+++ b/nova/tests/public_key/dummy.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAMGJlY9XEIm2X234pdO5yFWMp2JuOQx8U0E815IVXhmKxYCBK9ZakgZOIQmPbXoGYyV+mziDPp6HJ0wKYLQxkwLEFr51fAZjWQvRss0SinURRuLkockDfGFtD4pYJthekr/rlqMKlBSDUSpGq8jUWW60UJ18FGooFpxR7ESqQRx/AAAAFQC96LRglaUeeP+E8U/yblEJocuiWwAAAIA3XiMR8Skiz/0aBm5K50SeQznQuMJTyzt9S9uaz5QZWiFu69hOyGSFGw8fqgxEkXFJIuHobQQpGYQubLW0NdaYRqyE/Vud3JUJUb8Texld6dz8vGemyB5d1YvtSeHIo8/BGv2msOqR3u5AZTaGCBD9DhpSGOKHEdNjTtvpPd8S8gAAAIBociGZ5jf09iHLVENhyXujJbxfGRPsyNTyARJfCOGl0oFV6hEzcQyw8U/ePwjgvjc2UizMWLl8tsb2FXKHRdc2v+ND3Us+XqKQ33X3ADP4FZ/+Oj213gMyhCmvFTP0u5FmHog9My4CB7YcIWRuUR42WlhQ2IfPvKwUoTk3R+T6Og== www-data@mk
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index c8559615a..54c0454de 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -354,6 +354,36 @@ class CloudTestCase(test.TestCase):
self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys))
self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys))
+ def test_import_public_key(self):
+ # test when user provides all values
+ result1 = self.cloud.import_public_key(self.context,
+ 'testimportkey1',
+ 'mytestpubkey',
+ 'mytestfprint')
+ self.assertTrue(result1)
+ keydata = db.key_pair_get(self.context,
+ self.context.user.id,
+ 'testimportkey1')
+ self.assertEqual('mytestpubkey', keydata['public_key'])
+ self.assertEqual('mytestfprint', keydata['fingerprint'])
+ # test when user omits fingerprint
+ pubkey_path = os.path.join(os.path.dirname(__file__), 'public_key')
+ f = open(pubkey_path + '/dummy.pub', 'r')
+ dummypub = f.readline().rstrip()
+ f.close
+ f = open(pubkey_path + '/dummy.fingerprint', 'r')
+ dummyfprint = f.readline().rstrip()
+ f.close
+ result2 = self.cloud.import_public_key(self.context,
+ 'testimportkey2',
+ dummypub)
+ self.assertTrue(result2)
+ keydata = db.key_pair_get(self.context,
+ self.context.user.id,
+ 'testimportkey2')
+ self.assertEqual(dummypub, keydata['public_key'])
+ self.assertEqual(dummyfprint, keydata['fingerprint'])
+
def test_delete_key_pair(self):
self._create_key('test')
self.cloud.delete_key_pair(self.context, 'test')
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 55e7ae0c4..9170837b6 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -334,6 +334,28 @@ class ComputeTestCase(test.TestCase):
self.compute.terminate_instance(self.context, instance_id)
+ def test_finish_resize(self):
+ """Contrived test to ensure finish_resize doesn't raise anything"""
+
+ def fake(*args, **kwargs):
+ pass
+
+ self.stubs.Set(self.compute.driver, 'finish_resize', fake)
+ context = self.context.elevated()
+ instance_id = self._create_instance()
+ self.compute.prep_resize(context, instance_id, 1)
+ migration_ref = db.migration_get_by_instance_and_status(context,
+ instance_id, 'pre-migrating')
+ try:
+ self.compute.finish_resize(context, instance_id,
+ int(migration_ref['id']), {})
+ except KeyError, e:
+ # Only catch key errors. We want other reasons for the test to
+ # fail to actually error out so we don't obscure anything
+ self.fail()
+
+ self.compute.terminate_instance(self.context, instance_id)
+
def test_resize_instance(self):
"""Ensure instance can be migrated/resized"""
instance_id = self._create_instance()
diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py
new file mode 100644
index 000000000..b6b0fcc68
--- /dev/null
+++ b/nova/tests/test_notifier.py
@@ -0,0 +1,117 @@
+# Copyright 2011 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import nova
+
+from nova import context
+from nova import flags
+from nova import rpc
+import nova.notifier.api
+from nova.notifier.api import notify
+from nova.notifier import no_op_notifier
+from nova.notifier import rabbit_notifier
+from nova import test
+
+import stubout
+
+
+class NotifierTestCase(test.TestCase):
+ """Test case for notifications"""
+ def setUp(self):
+ super(NotifierTestCase, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(NotifierTestCase, self).tearDown()
+
+ def test_send_notification(self):
+ self.notify_called = False
+
+ def mock_notify(cls, *args):
+ self.notify_called = True
+
+ self.stubs.Set(nova.notifier.no_op_notifier, 'notify',
+ mock_notify)
+
+ class Mock(object):
+ pass
+ notify('publisher_id', 'event_type',
+ nova.notifier.api.WARN, dict(a=3))
+ self.assertEqual(self.notify_called, True)
+
+ def test_verify_message_format(self):
+ """A test to ensure changing the message format is prohibitively
+ annoying"""
+
+ def message_assert(message):
+ fields = [('publisher_id', 'publisher_id'),
+ ('event_type', 'event_type'),
+ ('priority', 'WARN'),
+ ('payload', dict(a=3))]
+ for k, v in fields:
+ self.assertEqual(message[k], v)
+ self.assertTrue(len(message['message_id']) > 0)
+ self.assertTrue(len(message['timestamp']) > 0)
+
+ self.stubs.Set(nova.notifier.no_op_notifier, 'notify',
+ message_assert)
+ notify('publisher_id', 'event_type',
+ nova.notifier.api.WARN, dict(a=3))
+
+ def test_send_rabbit_notification(self):
+ self.stubs.Set(nova.flags.FLAGS, 'notification_driver',
+ 'nova.notifier.rabbit_notifier')
+ self.mock_cast = False
+
+ def mock_cast(cls, *args):
+ self.mock_cast = True
+
+ class Mock(object):
+ pass
+
+ self.stubs.Set(nova.rpc, 'cast', mock_cast)
+ notify('publisher_id', 'event_type',
+ nova.notifier.api.WARN, dict(a=3))
+
+ self.assertEqual(self.mock_cast, True)
+
+ def test_invalid_priority(self):
+ def mock_cast(cls, *args):
+ pass
+
+ class Mock(object):
+ pass
+
+ self.stubs.Set(nova.rpc, 'cast', mock_cast)
+ self.assertRaises(nova.notifier.api.BadPriorityException,
+ notify, 'publisher_id',
+ 'event_type', 'not a priority', dict(a=3))
+
+ def test_rabbit_priority_queue(self):
+ self.stubs.Set(nova.flags.FLAGS, 'notification_driver',
+ 'nova.notifier.rabbit_notifier')
+ self.stubs.Set(nova.flags.FLAGS, 'notification_topic',
+ 'testnotify')
+
+ self.test_topic = None
+
+ def mock_cast(context, topic, msg):
+ self.test_topic = topic
+
+ self.stubs.Set(nova.rpc, 'cast', mock_cast)
+ notify('publisher_id',
+ 'event_type', 'DEBUG', dict(a=3))
+ self.assertEqual(self.test_topic, 'testnotify.debug')
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 968ef9d6c..54b3f80fb 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -912,7 +912,8 @@ class SimpleDriverTestCase(test.TestCase):
class FakeZone(object):
- def __init__(self, api_url, username, password):
+ def __init__(self, id, api_url, username, password):
+ self.id = id
self.api_url = api_url
self.username = username
self.password = password
@@ -920,7 +921,7 @@ class FakeZone(object):
def zone_get_all(context):
return [
- FakeZone('http://example.com', 'bob', 'xxx'),
+ FakeZone(1, 'http://example.com', 'bob', 'xxx'),
]
@@ -1037,7 +1038,7 @@ class FakeNovaClient(object):
class DynamicNovaClientTest(test.TestCase):
def test_issue_novaclient_command_found(self):
- zone = FakeZone('http://example.com', 'bob', 'xxx')
+ zone = FakeZone(1, 'http://example.com', 'bob', 'xxx')
self.assertEquals(api._issue_novaclient_command(
FakeNovaClient(FakeServerCollection()),
zone, "servers", "get", 100).a, 10)
@@ -1051,7 +1052,7 @@ class DynamicNovaClientTest(test.TestCase):
zone, "servers", "pause", 100), None)
def test_issue_novaclient_command_not_found(self):
- zone = FakeZone('http://example.com', 'bob', 'xxx')
+ zone = FakeZone(1, 'http://example.com', 'bob', 'xxx')
self.assertEquals(api._issue_novaclient_command(
FakeNovaClient(FakeEmptyServerCollection()),
zone, "servers", "get", 100), None)
@@ -1063,3 +1064,55 @@ class DynamicNovaClientTest(test.TestCase):
self.assertEquals(api._issue_novaclient_command(
FakeNovaClient(FakeEmptyServerCollection()),
zone, "servers", "any", "name"), None)
+
+
+class FakeZonesProxy(object):
+ def do_something(*args, **kwargs):
+ return 42
+
+ def raises_exception(*args, **kwargs):
+ raise Exception('testing')
+
+
+class FakeNovaClientOpenStack(object):
+ def __init__(self, *args, **kwargs):
+ self.zones = FakeZonesProxy()
+
+ def authenticate(self):
+ pass
+
+
+class CallZoneMethodTest(test.TestCase):
+ def setUp(self):
+ super(CallZoneMethodTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(db, 'zone_get_all', zone_get_all)
+ self.stubs.Set(novaclient, 'OpenStack', FakeNovaClientOpenStack)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ super(CallZoneMethodTest, self).tearDown()
+
+ def test_call_zone_method(self):
+ context = {}
+ method = 'do_something'
+ results = api.call_zone_method(context, method)
+ expected = [(1, 42)]
+ self.assertEqual(expected, results)
+
+ def test_call_zone_method_not_present(self):
+ context = {}
+ method = 'not_present'
+ self.assertRaises(AttributeError, api.call_zone_method,
+ context, method)
+
+ def test_call_zone_method_generates_exception(self):
+ context = {}
+ method = 'raises_exception'
+ results = api.call_zone_method(context, method)
+
+ # FIXME(sirp): for now the _error_trap code is catching errors and
+ # converting them to a ("ERROR", "string") tuples. The code (and this
+ # test) should eventually handle real exceptions.
+ expected = [(1, ('ERROR', 'testing'))]
+ self.assertEqual(expected, results)
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index d743f94f7..1bec9caca 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -849,7 +849,7 @@ class IptablesFirewallTestCase(test.TestCase):
self.assertEquals(len(rulesv4), 2)
self.assertEquals(len(rulesv6), 0)
- def multinic_iptables_test(self):
+ def test_multinic_iptables(self):
ipv4_rules_per_network = 2
ipv6_rules_per_network = 3
networks_count = 5
@@ -869,6 +869,16 @@ class IptablesFirewallTestCase(test.TestCase):
self.assertEquals(ipv6_network_rules,
ipv6_rules_per_network * networks_count)
+ def test_do_refresh_security_group_rules(self):
+ instance_ref = self._create_instance_ref()
+ self.mox.StubOutWithMock(self.fw,
+ 'add_filters_for_instance',
+ use_mock_anything=True)
+ self.fw.add_filters_for_instance(instance_ref, mox.IgnoreArg())
+ self.fw.instances[instance_ref['id']] = instance_ref
+ self.mox.ReplayAll()
+ self.fw.do_refresh_security_group_rules("fake")
+
class NWFilterTestCase(test.TestCase):
def setUp(self):
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index ddea1a1f7..f8aea1f34 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -81,34 +81,36 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
else:
mapped_device = device
- # We can only loopback mount raw images. If the device isn't there,
- # it's normally because it's a .vmdk or a .vdi etc
- if not os.path.exists(mapped_device):
- raise exception.Error('Mapped device was not found (we can'
- ' only inject raw disk images): %s' %
- mapped_device)
-
- # Configure ext2fs so that it doesn't auto-check every N boots
- out, err = utils.execute('sudo', 'tune2fs',
- '-c', 0, '-i', 0, mapped_device)
-
- tmpdir = tempfile.mkdtemp()
try:
- # mount loopback to dir
- out, err = utils.execute(
- 'sudo', 'mount', mapped_device, tmpdir)
- if err:
- raise exception.Error(_('Failed to mount filesystem: %s')
- % err)
-
+ # We can only loopback mount raw images. If the device isn't there,
+ # it's normally because it's a .vmdk or a .vdi etc
+ if not os.path.exists(mapped_device):
+ raise exception.Error('Mapped device was not found (we can'
+ ' only inject raw disk images): %s' %
+ mapped_device)
+
+ # Configure ext2fs so that it doesn't auto-check every N boots
+ out, err = utils.execute('sudo', 'tune2fs',
+ '-c', 0, '-i', 0, mapped_device)
+
+ tmpdir = tempfile.mkdtemp()
try:
- inject_data_into_fs(tmpdir, key, net, utils.execute)
+ # mount loopback to dir
+ out, err = utils.execute(
+ 'sudo', 'mount', mapped_device, tmpdir)
+ if err:
+ raise exception.Error(_('Failed to mount filesystem: %s')
+ % err)
+
+ try:
+ inject_data_into_fs(tmpdir, key, net, utils.execute)
+ finally:
+ # unmount device
+ utils.execute('sudo', 'umount', mapped_device)
finally:
- # unmount device
- utils.execute('sudo', 'umount', mapped_device)
+ # remove temporary directory
+ utils.execute('rmdir', tmpdir)
finally:
- # remove temporary directory
- utils.execute('rmdir', tmpdir)
if not partition is None:
# remove partitions
utils.execute('sudo', 'kpartx', '-d', device)
diff --git a/nova/virt/images.py b/nova/virt/images.py
index 2e3f2ee4d..02c898fda 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -21,19 +21,10 @@
Handling of VM disk images.
"""
-import os.path
-import shutil
-import sys
-import time
-import urllib2
-import urlparse
-
from nova import context
from nova import flags
from nova import log as logging
from nova import utils
-from nova.auth import manager
-from nova.auth import signer
FLAGS = flags.FLAGS
@@ -52,66 +43,6 @@ def fetch(image_id, path, _user, _project):
return metadata
-# NOTE(vish): The methods below should be unnecessary, but I'm leaving
-# them in case the glance client does not work on windows.
-def _fetch_image_no_curl(url, path, headers):
- request = urllib2.Request(url)
- for (k, v) in headers.iteritems():
- request.add_header(k, v)
-
- def urlretrieve(urlfile, fpath):
- chunk = 1 * 1024 * 1024
- f = open(fpath, "wb")
- while 1:
- data = urlfile.read(chunk)
- if not data:
- break
- f.write(data)
-
- urlopened = urllib2.urlopen(request)
- urlretrieve(urlopened, path)
- LOG.debug(_("Finished retreving %(url)s -- placed in %(path)s") % locals())
-
-
-def _fetch_s3_image(image, path, user, project):
- url = image_url(image)
-
- # This should probably move somewhere else, like e.g. a download_as
- # method on User objects and at the same time get rewritten to use
- # a web client.
- headers = {}
- headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
-
- (_, _, url_path, _, _, _) = urlparse.urlparse(url)
- access = manager.AuthManager().get_access_key(user, project)
- signature = signer.Signer(user.secret.encode()).s3_authorization(headers,
- 'GET',
- url_path)
- headers['Authorization'] = 'AWS %s:%s' % (access, signature)
-
- if sys.platform.startswith('win'):
- return _fetch_image_no_curl(url, path, headers)
- else:
- cmd = ['/usr/bin/curl', '--fail', '--silent', url]
- for (k, v) in headers.iteritems():
- cmd += ['-H', '\'%s: %s\'' % (k, v)]
-
- cmd += ['-o', path]
- return utils.execute(*cmd)
-
-
-def _fetch_local_image(image, path, user, project):
- source = _image_path(os.path.join(image, 'image'))
- if sys.platform.startswith('win'):
- return shutil.copy(source, path)
- else:
- return utils.execute('cp', source, path)
-
-
-def _image_path(path):
- return os.path.join(FLAGS.images_path, path)
-
-
# TODO(vish): xenapi should use the glance client code directly instead
# of retrieving the image using this method.
def image_url(image):
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 6ee23d1df..fa918b0a3 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1614,7 +1614,9 @@ class FirewallDriver(object):
"""
raise NotImplementedError()
- def refresh_security_group_rules(self, security_group_id):
+ def refresh_security_group_rules(self,
+ security_group_id,
+ network_info=None):
"""Refresh security group rules from data store
Gets called when a rule has been added to or removed from
@@ -1913,7 +1915,9 @@ class NWFilterFirewall(FirewallDriver):
self._define_filter(self._filter_container(filter_name,
filter_children))
- def refresh_security_group_rules(self, security_group_id):
+ def refresh_security_group_rules(self,
+ security_group_id,
+ network_info=None):
return self._define_filter(
self.security_group_to_nwfilter_xml(security_group_id))
@@ -2171,15 +2175,19 @@ class IptablesFirewallDriver(FirewallDriver):
def refresh_security_group_members(self, security_group):
pass
- def refresh_security_group_rules(self, security_group):
- self.do_refresh_security_group_rules(security_group)
+ def refresh_security_group_rules(self, security_group, network_info=None):
+ self.do_refresh_security_group_rules(security_group, network_info)
self.iptables.apply()
@utils.synchronized('iptables', external=True)
- def do_refresh_security_group_rules(self, security_group):
+ def do_refresh_security_group_rules(self,
+ security_group,
+ network_info=None):
for instance in self.instances.values():
self.remove_filters_for_instance(instance)
- self.add_filters_for_instance(instance)
+ if not network_info:
+ network_info = _get_network_info(instance)
+ self.add_filters_for_instance(instance, network_info)
def _security_group_chain_name(self, security_group_id):
return 'nova-sg-%s' % (security_group_id,)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index c8f342aa8..9f6cd608c 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -48,6 +48,8 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('default_os_type', 'linux', 'Default OS type')
flags.DEFINE_integer('block_device_creation_timeout', 10,
'time to wait for a block device to be created')
+flags.DEFINE_integer('max_kernel_ramdisk_size', 16 * 1024 * 1024,
+ 'maximum size in bytes of kernel or ramdisk images')
XENAPI_POWER_STATE = {
'Halted': power_state.SHUTDOWN,
@@ -444,6 +446,12 @@ class VMHelper(HelperBase):
if image_type == ImageType.DISK:
# Make room for MBR.
vdi_size += MBR_SIZE_BYTES
+ elif image_type == ImageType.KERNEL_RAMDISK and \
+ vdi_size > FLAGS.max_kernel_ramdisk_size:
+ max_size = FLAGS.max_kernel_ramdisk_size
+ raise exception.Error(
+ _("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, "
+ "max %(max_size)d bytes") % locals())
name_label = get_name_label_for_image(image)
vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 13d7d215b..0074444f8 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -25,7 +25,6 @@ import M2Crypto
import os
import pickle
import subprocess
-import tempfile
import uuid
from nova import context
@@ -1163,18 +1162,17 @@ class SimpleDH(object):
return mpi
def _run_ssl(self, text, which):
- base_cmd = ('cat %(tmpfile)s | openssl enc -aes-128-cbc '
- '-a -pass pass:%(shared)s -nosalt %(dec_flag)s')
+ base_cmd = ('openssl enc -aes-128-cbc -a -pass pass:%(shared)s '
+ '-nosalt %(dec_flag)s')
if which.lower()[0] == 'd':
dec_flag = ' -d'
else:
dec_flag = ''
- fd, tmpfile = tempfile.mkstemp()
- os.close(fd)
- file(tmpfile, 'w').write(text)
shared = self._shared
cmd = base_cmd % locals()
proc = _runproc(cmd)
+ proc.stdin.write(text)
+ proc.stdin.close()
proc.wait()
err = proc.stderr.read()
if err:
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index eb572f295..6d828e109 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -169,15 +169,15 @@ class XenAPIConnection(driver.ComputeDriver):
def __init__(self, url, user, pw):
super(XenAPIConnection, self).__init__()
- session = XenAPISession(url, user, pw)
- self._vmops = VMOps(session)
- self._volumeops = VolumeOps(session)
+ self._session = XenAPISession(url, user, pw)
+ self._vmops = VMOps(self._session)
+ self._volumeops = VolumeOps(self._session)
self._host_state = None
@property
def HostState(self):
if not self._host_state:
- self._host_state = HostState(self.session)
+ self._host_state = HostState(self._session)
return self._host_state
def init_host(self, host):
diff --git a/nova/wsgi.py b/nova/wsgi.py
index e60a8820d..ea9bb963d 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -59,13 +59,16 @@ class Server(object):
def __init__(self, threads=1000):
self.pool = eventlet.GreenPool(threads)
+ self.socket_info = {}
- def start(self, application, port, host='0.0.0.0', backlog=128):
+ def start(self, application, port, host='0.0.0.0', key=None, backlog=128):
"""Run a WSGI server with the given application."""
arg0 = sys.argv[0]
logging.audit(_('Starting %(arg0)s on %(host)s:%(port)s') % locals())
socket = eventlet.listen((host, port), backlog=backlog)
self.pool.spawn_n(self._run, application, socket)
+ if key:
+ self.socket_info[key] = socket.getsockname()
def wait(self):
"""Wait until all servers have completed running."""
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 0a45f3873..4b45671ae 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -68,12 +68,12 @@ def _download_tarball(sr_path, staging_path, image_id, glance_host,
area.
"""
conn = httplib.HTTPConnection(glance_host, glance_port)
- conn.request('GET', '/images/%s' % image_id)
+ conn.request('GET', '/v1/images/%s' % image_id)
resp = conn.getresponse()
if resp.status == httplib.NOT_FOUND:
raise Exception("Image '%s' not found in Glance" % image_id)
elif resp.status != httplib.OK:
- raise Exception("Unexpected response from Glance %i" % res.status)
+ raise Exception("Unexpected response from Glance %i" % resp.status)
tar_cmd = "tar -zx --directory=%(staging_path)s" % locals()
tar_proc = _make_subprocess(tar_cmd, stderr=True, stdin=True)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
new file mode 100644
index 000000000..a8428e841
--- /dev/null
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+
+# Copyright 2011 OpenStack LLC.
+# Copyright 2011 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.
+
+#
+# XenAPI plugin for reading/writing information to xenstore
+#
+
+try:
+ import json
+except ImportError:
+ import simplejson as json
+import os
+import random
+import re
+import subprocess
+import tempfile
+import time
+
+import XenAPIPlugin
+
+from pluginlib_nova import *
+configure_logging("xenhost")
+
+host_data_pattern = re.compile(r"\s*(\S+) \([^\)]+\) *: ?(.*)")
+
+
+def jsonify(fnc):
+ def wrapper(*args, **kwargs):
+ return json.dumps(fnc(*args, **kwargs))
+ return wrapper
+
+
+class TimeoutError(StandardError):
+ pass
+
+
+def _run_command(cmd):
+ """Abstracts out the basics of issuing system commands. If the command
+ returns anything in stderr, a PluginError is raised with that information.
+ Otherwise, the output from stdout is returned.
+ """
+ pipe = subprocess.PIPE
+ proc = subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe,
+ stderr=pipe, close_fds=True)
+ proc.wait()
+ err = proc.stderr.read()
+ if err:
+ raise pluginlib.PluginError(err)
+ return proc.stdout.read()
+
+
+@jsonify
+def host_data(self, arg_dict):
+ """Runs the commands on the xenstore host to return the current status
+ information.
+ """
+ cmd = "xe host-list | grep uuid"
+ resp = _run_command(cmd)
+ host_uuid = resp.split(":")[-1].strip()
+ cmd = "xe host-param-list uuid=%s" % host_uuid
+ resp = _run_command(cmd)
+ parsed_data = parse_response(resp)
+ # We have the raw dict of values. Extract those that we need,
+ # and convert the data types as needed.
+ ret_dict = cleanup(parsed_data)
+ return ret_dict
+
+
+def parse_response(resp):
+ data = {}
+ for ln in resp.splitlines():
+ if not ln:
+ continue
+ mtch = host_data_pattern.match(ln.strip())
+ try:
+ k, v = mtch.groups()
+ data[k] = v
+ except AttributeError:
+ # Not a valid line; skip it
+ continue
+ return data
+
+
+def cleanup(dct):
+ """Take the raw KV pairs returned and translate them into the
+ appropriate types, discarding any we don't need.
+ """
+ def safe_int(val):
+ """Integer values will either be string versions of numbers,
+ or empty strings. Convert the latter to nulls.
+ """
+ try:
+ return int(val)
+ except ValueError:
+ return None
+
+ def strip_kv(ln):
+ return [val.strip() for val in ln.split(":", 1)]
+
+ out = {}
+
+# sbs = dct.get("supported-bootloaders", "")
+# out["host_supported-bootloaders"] = sbs.split("; ")
+# out["host_suspend-image-sr-uuid"] = dct.get("suspend-image-sr-uuid", "")
+# out["host_crash-dump-sr-uuid"] = dct.get("crash-dump-sr-uuid", "")
+# out["host_local-cache-sr"] = dct.get("local-cache-sr", "")
+ out["host_memory"] = omm = {}
+ omm["total"] = safe_int(dct.get("memory-total", ""))
+ omm["overhead"] = safe_int(dct.get("memory-overhead", ""))
+ omm["free"] = safe_int(dct.get("memory-free", ""))
+ omm["free-computed"] = safe_int(
+ dct.get("memory-free-computed", ""))
+
+# out["host_API-version"] = avv = {}
+# avv["vendor"] = dct.get("API-version-vendor", "")
+# avv["major"] = safe_int(dct.get("API-version-major", ""))
+# avv["minor"] = safe_int(dct.get("API-version-minor", ""))
+
+ out["host_uuid"] = dct.get("uuid", None)
+ out["host_name-label"] = dct.get("name-label", "")
+ out["host_name-description"] = dct.get("name-description", "")
+# out["host_host-metrics-live"] = dct.get(
+# "host-metrics-live", "false") == "true"
+ out["host_hostname"] = dct.get("hostname", "")
+ out["host_ip_address"] = dct.get("address", "")
+ oc = dct.get("other-config", "")
+ out["host_other-config"] = ocd = {}
+ if oc:
+ for oc_fld in oc.split("; "):
+ ock, ocv = strip_kv(oc_fld)
+ ocd[ock] = ocv
+# out["host_capabilities"] = dct.get("capabilities", "").split("; ")
+# out["host_allowed-operations"] = dct.get(
+# "allowed-operations", "").split("; ")
+# lsrv = dct.get("license-server", "")
+# out["host_license-server"] = ols = {}
+# if lsrv:
+# for lspart in lsrv.split("; "):
+# lsk, lsv = lspart.split(": ")
+# if lsk == "port":
+# ols[lsk] = safe_int(lsv)
+# else:
+# ols[lsk] = lsv
+# sv = dct.get("software-version", "")
+# out["host_software-version"] = osv = {}
+# if sv:
+# for svln in sv.split("; "):
+# svk, svv = strip_kv(svln)
+# osv[svk] = svv
+ cpuinf = dct.get("cpu_info", "")
+ out["host_cpu_info"] = ocp = {}
+ if cpuinf:
+ for cpln in cpuinf.split("; "):
+ cpk, cpv = strip_kv(cpln)
+ if cpk in ("cpu_count", "family", "model", "stepping"):
+ ocp[cpk] = safe_int(cpv)
+ else:
+ ocp[cpk] = cpv
+# out["host_edition"] = dct.get("edition", "")
+# out["host_external-auth-service-name"] = dct.get(
+# "external-auth-service-name", "")
+ return out
+
+
+if __name__ == "__main__":
+ XenAPIPlugin.dispatch(
+ {"host_data": host_data})
diff --git a/run_tests.sh b/run_tests.sh
index e3a0bd243..9aa555484 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -59,7 +59,13 @@ function run_tests {
function run_pep8 {
echo "Running pep8 ..."
+ # Opt-out files from pep8
+ ignore_scripts="*.sh:*nova-debug:*clean-vlans"
+ ignore_files="*eventlet-patch:*pip-requires"
+ ignore_dirs="*ajaxterm*"
+ GLOBIGNORE="$ignore_scripts:$ignore_files:$ignore_dirs"
srcfiles=`find bin -type f ! -name "nova.conf*"`
+ srcfiles+=" `find tools/*`"
srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance"
pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py ${srcfiles}
}
diff --git a/tools/install_venv.py b/tools/install_venv.py
index 8149a3afa..812b1dd0f 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -31,119 +31,125 @@ import sys
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
VENV = os.path.join(ROOT, '.nova-venv')
PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
-TWISTED_NOVA='http://nova.openstack.org/Twisted-10.0.0Nova.tar.gz'
-PY_VERSION = "python" + str(sys.version_info[0]) + '.' + str(sys.version_info[1])
+TWISTED_NOVA = 'http://nova.openstack.org/Twisted-10.0.0Nova.tar.gz'
+PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
+
def die(message, *args):
- print >>sys.stderr, message % args
- sys.exit(1)
+ print >>sys.stderr, message % args
+ sys.exit(1)
+
def check_python_version():
- if sys.version_info < (2, 6):
- die("Need Python Version >= 2.6")
+ if sys.version_info < (2, 6):
+ die("Need Python Version >= 2.6")
+
def run_command(cmd, redirect_output=True, check_exit_code=True):
- """
- Runs a command in an out-of-process shell, returning the
- output of that command. Working directory is ROOT.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
+ """
+ Runs a command in an out-of-process shell, returning the
+ output of that command. Working directory is ROOT.
+ """
+ if redirect_output:
+ stdout = subprocess.PIPE
+ else:
+ stdout = None
- proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- return output
+ proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
+ output = proc.communicate()[0]
+ if check_exit_code and proc.returncode != 0:
+ die('Command "%s" failed.\n%s', ' '.join(cmd), output)
+ return output
-HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], check_exit_code=False).strip())
-HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], check_exit_code=False).strip())
+HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
+ check_exit_code=False).strip())
+HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
+ check_exit_code=False).strip())
def check_dependencies():
- """Make sure virtualenv is in the path."""
-
- if not HAS_VIRTUALENV:
- print 'not found.'
- # Try installing it via easy_install...
- if HAS_EASY_INSTALL:
- print 'Installing virtualenv via easy_install...',
- if not (run_command(['which', 'easy_install']) and
- run_command(['easy_install', 'virtualenv'])):
- die('ERROR: virtualenv not found.\n\nNova development requires virtualenv,'
- ' please install it using your favorite package management tool')
- print 'done.'
- print 'done.'
+ """Make sure virtualenv is in the path."""
+
+ if not HAS_VIRTUALENV:
+ print 'not found.'
+ # Try installing it via easy_install...
+ if HAS_EASY_INSTALL:
+ print 'Installing virtualenv via easy_install...',
+ if not (run_command(['which', 'easy_install']) and
+ run_command(['easy_install', 'virtualenv'])):
+ die('ERROR: virtualenv not found.\n\nNova development'
+ ' requires virtualenv, please install it using your'
+ ' favorite package management tool')
+ print 'done.'
+ print 'done.'
def create_virtualenv(venv=VENV):
- """Creates the virtual environment and installs PIP only into the
- virtual environment
- """
- print 'Creating venv...',
- run_command(['virtualenv', '-q', '--no-site-packages', VENV])
- print 'done.'
- print 'Installing pip in virtualenv...',
- if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip():
- die("Failed to install pip.")
- print 'done.'
+ """Creates the virtual environment and installs PIP only into the
+ virtual environment
+ """
+ print 'Creating venv...',
+ run_command(['virtualenv', '-q', '--no-site-packages', VENV])
+ print 'done.'
+ print 'Installing pip in virtualenv...',
+ if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip():
+ die("Failed to install pip.")
+ print 'done.'
def install_dependencies(venv=VENV):
- print 'Installing dependencies with pip (this can take a while)...'
- # Install greenlet by hand - just listing it in the requires file does not
- # get it in stalled in the right order
- run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, 'greenlet'],
- redirect_output=False)
- run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, '-r', PIP_REQUIRES],
- redirect_output=False)
- run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, TWISTED_NOVA],
- redirect_output=False)
-
-
- # Tell the virtual env how to "import nova"
- pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", "nova.pth")
- f = open(pthfile, 'w')
- f.write("%s\n" % ROOT)
- # Patch eventlet (see FAQ # 1485)
- patchsrc = os.path.join(ROOT, 'tools', 'eventlet-patch')
- patchfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", "eventlet",
- "green", "subprocess.py")
- patch_cmd = "patch %s %s" % (patchfile, patchsrc)
- os.system(patch_cmd)
+ print 'Installing dependencies with pip (this can take a while)...'
+ # Install greenlet by hand - just listing it in the requires file does not
+ # get it in stalled in the right order
+ run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv,
+ 'greenlet'], redirect_output=False)
+ run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, '-r',
+ PIP_REQUIRES], redirect_output=False)
+ run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv,
+ TWISTED_NOVA], redirect_output=False)
+
+ # Tell the virtual env how to "import nova"
+ pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages",
+ "nova.pth")
+ f = open(pthfile, 'w')
+ f.write("%s\n" % ROOT)
+ # Patch eventlet (see FAQ # 1485)
+ patchsrc = os.path.join(ROOT, 'tools', 'eventlet-patch')
+ patchfile = os.path.join(venv, "lib", PY_VERSION, "site-packages",
+ "eventlet", "green", "subprocess.py")
+ patch_cmd = "patch %s %s" % (patchfile, patchsrc)
+ os.system(patch_cmd)
def print_help():
- help = """
- Nova development environment setup is complete.
+ help = """
+ Nova development environment setup is complete.
- Nova development uses virtualenv to track and manage Python dependencies
- while in development and testing.
+ Nova development uses virtualenv to track and manage Python dependencies
+ while in development and testing.
- To activate the Nova virtualenv for the extent of your current shell session
- you can run:
+ To activate the Nova virtualenv for the extent of your current shell
+ session you can run:
- $ source .nova-venv/bin/activate
+ $ source .nova-venv/bin/activate
- Or, if you prefer, you can run commands in the virtualenv on a case by case
- basis by running:
+ Or, if you prefer, you can run commands in the virtualenv on a case by case
+ basis by running:
- $ tools/with_venv.sh <your command>
+ $ tools/with_venv.sh <your command>
- Also, make test will automatically use the virtualenv.
- """
- print help
+ Also, make test will automatically use the virtualenv.
+ """
+ print help
def main(argv):
- check_python_version()
- check_dependencies()
- create_virtualenv()
- install_dependencies()
- print_help()
+ check_python_version()
+ check_dependencies()
+ create_virtualenv()
+ install_dependencies()
+ print_help()
if __name__ == '__main__':
- main(sys.argv)
+ main(sys.argv)