summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.bzrignore1
-rw-r--r--Authors2
-rw-r--r--babel.cfg2
-rwxr-xr-xbin/nova-api-paste2
-rwxr-xr-xbin/nova-dhcpbridge17
-rwxr-xr-xbin/nova-instancemonitor7
-rw-r--r--bin/nova-logspool156
-rwxr-xr-xbin/nova-manage22
-rw-r--r--bin/nova-spoolsentry97
-rw-r--r--doc/source/conf.py8
-rw-r--r--locale/nova.pot2130
-rw-r--r--nova/api/__init__.py1
-rw-r--r--nova/api/ec2/__init__.py94
-rw-r--r--nova/api/ec2/admin.py38
-rw-r--r--nova/api/ec2/apirequest.py12
-rw-r--r--nova/api/ec2/cloud.py62
-rw-r--r--nova/api/ec2/metadatarequesthandler.py7
-rw-r--r--nova/api/openstack/__init__.py13
-rw-r--r--nova/api/openstack/servers.py65
-rw-r--r--nova/auth/dbdriver.py1
-rw-r--r--nova/auth/ldapdriver.py8
-rw-r--r--nova/auth/manager.py61
-rw-r--r--nova/auth/signer.py15
-rw-r--r--nova/cloudpipe/pipelib.py4
-rw-r--r--nova/compute/api.py123
-rw-r--r--nova/compute/disk.py11
-rw-r--r--nova/compute/manager.py193
-rw-r--r--nova/compute/monitor.py39
-rw-r--r--nova/crypto.py5
-rw-r--r--nova/db/api.py9
-rw-r--r--nova/db/sqlalchemy/__init__.py8
-rw-r--r--nova/db/sqlalchemy/api.py61
-rw-r--r--nova/db/sqlalchemy/models.py2
-rw-r--r--nova/exception.py7
-rw-r--r--nova/fakerabbit.py20
-rw-r--r--nova/flags.py28
-rw-r--r--nova/image/glance.py22
-rw-r--r--nova/log.py254
-rw-r--r--nova/network/api.py6
-rw-r--r--nova/network/linux_net.py23
-rw-r--r--nova/network/manager.py20
-rw-r--r--nova/objectstore/handler.py68
-rw-r--r--nova/rpc.py30
-rw-r--r--nova/scheduler/manager.py5
-rw-r--r--nova/service.py28
-rw-r--r--nova/tests/api/openstack/fakes.py2
-rw-r--r--nova/tests/api/openstack/test_images.py1
-rw-r--r--nova/tests/objectstore_unittest.py2
-rw-r--r--nova/tests/test_access.py1
-rw-r--r--nova/tests/test_auth.py9
-rw-r--r--nova/tests/test_cloud.py48
-rw-r--r--nova/tests/test_compute.py27
-rw-r--r--nova/tests/test_log.py110
-rw-r--r--nova/tests/test_network.py9
-rw-r--r--nova/tests/test_quota.py4
-rw-r--r--nova/tests/test_rpc.py11
-rw-r--r--nova/tests/test_virt.py100
-rw-r--r--nova/tests/test_volume.py6
-rw-r--r--nova/tests/xenapi/stubs.py24
-rw-r--r--nova/twistd.py25
-rw-r--r--nova/utils.py28
-rw-r--r--nova/version.py46
-rw-r--r--nova/virt/connection.py5
-rw-r--r--nova/virt/hyperv.py65
-rw-r--r--nova/virt/images.py8
-rw-r--r--nova/virt/libvirt_conn.py442
-rw-r--r--nova/virt/xenapi/fake.py24
-rw-r--r--nova/virt/xenapi/vm_utils.py68
-rw-r--r--nova/virt/xenapi/vmops.py26
-rw-r--r--nova/virt/xenapi/volume_utils.py44
-rw-r--r--nova/virt/xenapi/volumeops.py31
-rw-r--r--nova/virt/xenapi_conn.py32
-rw-r--r--nova/volume/api.py10
-rw-r--r--nova/volume/driver.py16
-rw-r--r--nova/volume/manager.py21
-rw-r--r--nova/wsgi.py19
-rw-r--r--setup.cfg14
-rw-r--r--setup.py28
-rw-r--r--smoketests/admin_smoketests.py9
-rw-r--r--smoketests/user_smoketests.py87
80 files changed, 4509 insertions, 680 deletions
diff --git a/.bzrignore b/.bzrignore
index d81a7d829..b271561a3 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -12,3 +12,4 @@ CA/openssl.cnf
CA/serial*
CA/newcerts/*.pem
CA/private/cakey.pem
+nova/vcsversion.py
diff --git a/Authors b/Authors
index e67363308..5954eedb9 100644
--- a/Authors
+++ b/Authors
@@ -24,6 +24,7 @@ Jonathan Bryce <jbryce@jbryce.com>
Josh Kearney <josh.kearney@rackspace.com>
Joshua McKenty <jmckenty@gmail.com>
Justin Santa Barbara <justin@fathomdb.com>
+Ken Pepple <ken.pepple@gmail.com>
Matt Dietz <matt.dietz@rackspace.com>
Michael Gundlach <michael.gundlach@rackspace.com>
Monty Taylor <mordred@inaugust.com>
@@ -41,4 +42,3 @@ Trey Morris <trey.morris@rackspace.com>
Vishvananda Ishaya <vishvananda@gmail.com>
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
Zhixue Wu <Zhixue.Wu@citrix.com>
-
diff --git a/babel.cfg b/babel.cfg
new file mode 100644
index 000000000..15cd6cb76
--- /dev/null
+++ b/babel.cfg
@@ -0,0 +1,2 @@
+[python: **.py]
+
diff --git a/bin/nova-api-paste b/bin/nova-api-paste
index 6ee833a18..419f0bbdc 100755
--- a/bin/nova-api-paste
+++ b/bin/nova-api-paste
@@ -21,7 +21,6 @@
"""Starter script for Nova API."""
import gettext
-import logging
import os
import sys
@@ -38,6 +37,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
gettext.install('nova', unicode=1)
from nova import flags
+from nova import log as logging
from nova import wsgi
LOG = logging.getLogger('nova.api')
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index 828aba3d1..1a994d956 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -22,7 +22,6 @@ Handle lease database updates from DHCP servers.
"""
import gettext
-import logging
import os
import sys
@@ -39,6 +38,7 @@ gettext.install('nova', unicode=1)
from nova import context
from nova import db
from nova import flags
+from nova import log as logging
from nova import rpc
from nova import utils
from nova.network import linux_net
@@ -49,11 +49,13 @@ flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('num_networks', 'nova.network.manager')
flags.DECLARE('update_dhcp_on_disassociate', 'nova.network.manager')
+LOG = logging.getLogger('nova.dhcpbridge')
+
def add_lease(mac, ip_address, _hostname, _interface):
"""Set the IP that was assigned by the DHCP server."""
if FLAGS.fake_rabbit:
- logging.debug("leasing ip")
+ LOG.debug(_("leasing ip"))
network_manager = utils.import_object(FLAGS.network_manager)
network_manager.lease_fixed_ip(context.get_admin_context(),
mac,
@@ -68,14 +70,14 @@ def add_lease(mac, ip_address, _hostname, _interface):
def old_lease(mac, ip_address, hostname, interface):
"""Update just as add lease."""
- logging.debug("Adopted old lease or got a change of mac/hostname")
+ LOG.debug(_("Adopted old lease or got a change of mac/hostname"))
add_lease(mac, ip_address, hostname, interface)
def del_lease(mac, ip_address, _hostname, _interface):
"""Called when a lease expires."""
if FLAGS.fake_rabbit:
- logging.debug("releasing ip")
+ LOG.debug(_("releasing ip"))
network_manager = utils.import_object(FLAGS.network_manager)
network_manager.release_fixed_ip(context.get_admin_context(),
mac,
@@ -100,6 +102,7 @@ def main():
flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile)
utils.default_flagfile(flagfile)
argv = FLAGS(sys.argv)
+ logging.basicConfig()
interface = os.environ.get('DNSMASQ_INTERFACE', 'br0')
if int(os.environ.get('TESTING', '0')):
FLAGS.fake_rabbit = True
@@ -117,9 +120,9 @@ def main():
mac = argv[2]
ip = argv[3]
hostname = argv[4]
- logging.debug("Called %s for mac %s with ip %s and "
- "hostname %s on interface %s",
- action, mac, ip, hostname, interface)
+ LOG.debug(_("Called %s for mac %s with ip %s and "
+ "hostname %s on interface %s"),
+ action, mac, ip, hostname, interface)
globals()[action + '_lease'](mac, ip, hostname, interface)
else:
print init_leases(interface)
diff --git a/bin/nova-instancemonitor b/bin/nova-instancemonitor
index 5dac3ffe6..7dca02014 100755
--- a/bin/nova-instancemonitor
+++ b/bin/nova-instancemonitor
@@ -23,7 +23,6 @@
import gettext
import os
-import logging
import sys
from twisted.application import service
@@ -37,19 +36,23 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
gettext.install('nova', unicode=1)
+from nova import log as logging
from nova import utils
from nova import twistd
from nova.compute import monitor
+# TODO(todd): shouldn't this be done with flags? And what about verbose?
logging.getLogger('boto').setLevel(logging.WARN)
+LOG = logging.getLogger('nova.instancemonitor')
+
if __name__ == '__main__':
utils.default_flagfile()
twistd.serve(__file__)
if __name__ == '__builtin__':
- logging.warn('Starting instance monitor')
+ LOG.warn(_('Starting instance monitor'))
# pylint: disable-msg=C0103
monitor = monitor.InstanceMonitor()
diff --git a/bin/nova-logspool b/bin/nova-logspool
new file mode 100644
index 000000000..097459b12
--- /dev/null
+++ b/bin/nova-logspool
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Tools for working with logs generated by nova components
+"""
+
+
+import json
+import os
+import re
+import sys
+
+
+class Request(object):
+ def __init__(self):
+ self.time = ""
+ self.host = ""
+ self.logger = ""
+ self.message = ""
+ self.trace = ""
+ self.env = ""
+ self.request_id = ""
+
+ def add_error_line(self, error_line):
+ self.time = " ".join(error_line.split(" ")[:3])
+ self.host = error_line.split(" ")[3]
+ self.logger = error_line.split("(")[1].split(" ")[0]
+ self.request_id = error_line.split("[")[1].split(" ")[0]
+ error_lines = error_line.split("#012")
+ self.message = self.clean_log_line(error_lines.pop(0))
+ self.trace = "\n".join([self.clean_trace(l) for l in error_lines])
+
+ def add_environment_line(self, env_line):
+ self.env = self.clean_env_line(env_line)
+
+ def clean_log_line(self, line):
+ """Remove log format for time, level, etc: split after context"""
+ return line.split('] ')[-1]
+
+ def clean_env_line(self, line):
+ """Also has an 'Environment: ' string in the message"""
+ return re.sub(r'^Environment: ', '', self.clean_log_line(line))
+
+ def clean_trace(self, line):
+ """trace has a different format, so split on TRACE:"""
+ return line.split('TRACE: ')[-1]
+
+ def to_dict(self):
+ return {'traceback': self.trace, 'message': self.message,
+ 'host': self.host, 'env': self.env, 'logger': self.logger,
+ 'request_id': self.request_id}
+
+
+class LogReader(object):
+ def __init__(self, filename):
+ self.filename = filename
+ self._errors = {}
+
+ def process(self, spooldir):
+ with open(self.filename) as f:
+ line = f.readline()
+ while len(line) > 0:
+ parts = line.split(" ")
+ level = (len(parts) < 6) or parts[5]
+ if level == 'ERROR':
+ self.handle_logged_error(line)
+ elif level == '[-]' and self.last_error:
+ # twisted stack trace line
+ clean_line = " ".join(line.split(" ")[6:])
+ self.last_error.trace = self.last_error.trace + clean_line
+ else:
+ self.last_error = None
+ line = f.readline()
+ self.update_spool(spooldir)
+
+ def handle_logged_error(self, line):
+ request_id = re.search(r' \[([A-Z0-9\-/]+)', line)
+ if not request_id:
+ raise Exception("Unable to parse request id from %s" % line)
+ request_id = request_id.group(1)
+ data = self._errors.get(request_id, Request())
+ if self.is_env_line(line):
+ data.add_environment_line(line)
+ elif self.is_error_line(line):
+ data.add_error_line(line)
+ else:
+ # possibly error from twsited
+ data.add_error_line(line)
+ self.last_error = data
+ self._errors[request_id] = data
+
+ def is_env_line(self, line):
+ return re.search('Environment: ', line)
+
+ def is_error_line(self, line):
+ return re.search('raised', line)
+
+ def update_spool(self, directory):
+ processed_dir = "%s/processed" % directory
+ self._ensure_dir_exists(processed_dir)
+ for rid, value in self._errors.iteritems():
+ if not self.has_been_processed(processed_dir, rid):
+ with open("%s/%s" % (directory, rid), "w") as spool:
+ spool.write(json.dumps(value.to_dict()))
+ self.flush_old_processed_spool(processed_dir)
+
+ def _ensure_dir_exists(self, d):
+ mkdir = False
+ try:
+ os.stat(d)
+ except:
+ mkdir = True
+ if mkdir:
+ os.mkdir(d)
+
+ def has_been_processed(self, processed_dir, rid):
+ rv = False
+ try:
+ os.stat("%s/%s" % (processed_dir, rid))
+ rv = True
+ except:
+ pass
+ return rv
+
+ def flush_old_processed_spool(self, processed_dir):
+ keys = self._errors.keys()
+ procs = os.listdir(processed_dir)
+ for p in procs:
+ if p not in keys:
+ # log has rotated and the old error won't be seen again
+ os.unlink("%s/%s" % (processed_dir, p))
+
+if __name__ == '__main__':
+ filename = '/var/log/nova.log'
+ spooldir = '/var/spool/nova'
+ if len(sys.argv) > 1:
+ filename = sys.argv[1]
+ if len(sys.argv) > 2:
+ spooldir = sys.argv[2]
+ LogReader(filename).process(spooldir)
diff --git a/bin/nova-manage b/bin/nova-manage
index 3416c1a52..3f5957190 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -55,8 +55,8 @@
import datetime
import gettext
-import logging
import os
+import re
import sys
import time
@@ -333,6 +333,11 @@ class ProjectCommands(object):
arguments: name project_manager [description]"""
self.manager.create_project(name, project_manager, description)
+ def modify(self, name, project_manager, description=None):
+ """Modifies a project
+ arguments: name project_manager [description]"""
+ self.manager.modify_project(name, project_manager, description)
+
def delete(self, name):
"""Deletes an existing project
arguments: name"""
@@ -499,6 +504,15 @@ class ServiceCommands(object):
db.service_update(ctxt, svc['id'], {'disabled': True})
+class LogCommands(object):
+ def request(self, request_id, logfile='/var/log/nova.log'):
+ """Show all fields in the log for the given request. Assumes you
+ haven't changed the log format too much.
+ ARGS: request_id [logfile]"""
+ lines = utils.execute("cat %s | grep '\[%s '" % (logfile, request_id))
+ print re.sub('#012', "\n", "\n".join(lines))
+
+
CATEGORIES = [
('user', UserCommands),
('project', ProjectCommands),
@@ -507,7 +521,8 @@ CATEGORIES = [
('vpn', VpnCommands),
('floating', FloatingIpCommands),
('network', NetworkCommands),
- ('service', ServiceCommands)]
+ ('service', ServiceCommands),
+ ('log', LogCommands)]
def lazy_match(name, key_value_tuples):
@@ -546,9 +561,6 @@ def main():
utils.default_flagfile()
argv = FLAGS(sys.argv)
- if FLAGS.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
-
script_name = argv.pop(0)
if len(argv) < 1:
print script_name + " category action [<args>]"
diff --git a/bin/nova-spoolsentry b/bin/nova-spoolsentry
new file mode 100644
index 000000000..ab20268a9
--- /dev/null
+++ b/bin/nova-spoolsentry
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+import base64
+import json
+import logging
+import os
+import shutil
+import sys
+import urllib
+import urllib2
+try:
+ import cPickle as pickle
+except:
+ import pickle
+
+
+class SpoolSentry(object):
+ def __init__(self, spool_dir, sentry_url, key=None):
+ self.spool_dir = spool_dir
+ self.sentry_url = sentry_url
+ self.key = key
+
+ def process(self):
+ for fname in os.listdir(self.spool_dir):
+ if fname == "processed":
+ continue
+ try:
+ sourcefile = "%s/%s" % (self.spool_dir, fname)
+ with open(sourcefile) as f:
+ fdata = f.read()
+ data_from_json = json.loads(fdata)
+ data = self.build_data(data_from_json)
+ self.send_data(data)
+ destfile = "%s/processed/%s" % (self.spool_dir, fname)
+ shutil.move(sourcefile, destfile)
+ except:
+ logging.exception("Unable to upload record %s", fname)
+ raise
+
+ def build_data(self, filejson):
+ env = {'SERVER_NAME': 'unknown', 'SERVER_PORT': '0000',
+ 'SCRIPT_NAME': '/unknown/', 'PATH_INFO': 'unknown'}
+ if filejson['env']:
+ env = json.loads(filejson['env'])
+ url = "http://%s:%s%s%s" % (env['SERVER_NAME'], env['SERVER_PORT'],
+ env['SCRIPT_NAME'], env['PATH_INFO'])
+ rv = {'logger': filejson['logger'], 'level': logging.ERROR,
+ 'server_name': filejson['host'], 'url': url,
+ 'message': filejson['message'],
+ 'traceback': filejson['traceback']}
+ rv['data'] = {}
+ if filejson['env']:
+ rv['data']['META'] = env
+ if filejson['request_id']:
+ rv['data']['request_id'] = filejson['request_id']
+ return rv
+
+ def send_data(self, data):
+ data = {
+ 'data': base64.b64encode(pickle.dumps(data).encode('zlib')),
+ 'key': self.key
+ }
+ req = urllib2.Request(self.sentry_url)
+ res = urllib2.urlopen(req, urllib.urlencode(data))
+ if res.getcode() != 200:
+ raise Exception("Bad HTTP code: %s" % res.getcode())
+ txt = res.read()
+
+if __name__ == '__main__':
+ sentryurl = 'http://127.0.0.1/sentry/store/'
+ key = ''
+ spooldir = '/var/spool/nova'
+ if len(sys.argv) > 1:
+ sentryurl = sys.argv[1]
+ if len(sys.argv) > 2:
+ key = sys.argv[2]
+ if len(sys.argv) > 3:
+ spooldir = sys.argv[3]
+ SpoolSentry(spooldir, sentryurl, key).process()
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 8f1b370cc..996dfb0a7 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -60,10 +60,12 @@ copyright = u'2010, United States Government as represented by the Administrator
# |version| and |release|, also used in various other places throughout the
# built documents.
#
-# The short X.Y version.
-version = '2011.1'
+from nova import version as nova_version
+#import nova.version
# The full version, including alpha/beta/rc tags.
-release = '2011.1-prerelease'
+release = nova_version.version_string()
+# The short X.Y version.
+version = nova_version.canonical_version_string()
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/locale/nova.pot b/locale/nova.pot
new file mode 100644
index 000000000..a96411e33
--- /dev/null
+++ b/locale/nova.pot
@@ -0,0 +1,2130 @@
+# Translations template for nova.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the nova project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: nova 2011.1\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-01-10 11:25-0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: nova/crypto.py:46
+msgid "Filename of root CA"
+msgstr ""
+
+#: nova/crypto.py:49
+msgid "Filename of private key"
+msgstr ""
+
+#: nova/crypto.py:51
+msgid "Filename of root Certificate Revokation List"
+msgstr ""
+
+#: nova/crypto.py:53
+msgid "Where we keep our keys"
+msgstr ""
+
+#: nova/crypto.py:55
+msgid "Where we keep our root CA"
+msgstr ""
+
+#: nova/crypto.py:57
+msgid "Should we use a CA for each project?"
+msgstr ""
+
+#: nova/crypto.py:61
+#, python-format
+msgid "Subject for certificate for users, %s for project, user, timestamp"
+msgstr ""
+
+#: nova/crypto.py:66
+#, python-format
+msgid "Subject for certificate for projects, %s for project, timestamp"
+msgstr ""
+
+#: nova/crypto.py:71
+#, python-format
+msgid "Subject for certificate for vpns, %s for project, timestamp"
+msgstr ""
+
+#: nova/crypto.py:258
+#, python-format
+msgid "Flags path: %s"
+msgstr ""
+
+#: nova/exception.py:33
+msgid "Unexpected error while running command."
+msgstr ""
+
+#: nova/exception.py:36
+#, python-format
+msgid ""
+"%s\n"
+"Command: %s\n"
+"Exit code: %s\n"
+"Stdout: %r\n"
+"Stderr: %r"
+msgstr ""
+
+#: nova/exception.py:86
+msgid "Uncaught exception"
+msgstr ""
+
+#: nova/fakerabbit.py:48
+#, python-format
+msgid "(%s) publish (key: %s) %s"
+msgstr ""
+
+#: nova/fakerabbit.py:53
+#, python-format
+msgid "Publishing to route %s"
+msgstr ""
+
+#: nova/fakerabbit.py:83
+#, python-format
+msgid "Declaring queue %s"
+msgstr ""
+
+#: nova/fakerabbit.py:89
+#, python-format
+msgid "Declaring exchange %s"
+msgstr ""
+
+#: nova/fakerabbit.py:95
+#, python-format
+msgid "Binding %s to %s with key %s"
+msgstr ""
+
+#: nova/fakerabbit.py:120
+#, python-format
+msgid "Getting from %s: %s"
+msgstr ""
+
+#: nova/rpc.py:92
+#, python-format
+msgid "AMQP server on %s:%d is unreachable. Trying again in %d seconds."
+msgstr ""
+
+#: nova/rpc.py:99
+#, python-format
+msgid "Unable to connect to AMQP server after %d tries. Shutting down."
+msgstr ""
+
+#: nova/rpc.py:118
+msgid "Reconnected to queue"
+msgstr ""
+
+#: nova/rpc.py:125
+msgid "Failed to fetch message from queue"
+msgstr ""
+
+#: nova/rpc.py:155
+#, python-format
+msgid "Initing the Adapter Consumer for %s"
+msgstr ""
+
+#: nova/rpc.py:170
+#, python-format
+msgid "received %s"
+msgstr ""
+
+#: nova/rpc.py:183
+#, python-format
+msgid "no method for message: %s"
+msgstr ""
+
+#: nova/rpc.py:184
+#, python-format
+msgid "No method for message: %s"
+msgstr ""
+
+#: nova/rpc.py:245
+#, python-format
+msgid "Returning exception %s to caller"
+msgstr ""
+
+#: nova/rpc.py:286
+#, python-format
+msgid "unpacked context: %s"
+msgstr ""
+
+#: nova/rpc.py:305
+msgid "Making asynchronous call..."
+msgstr ""
+
+#: nova/rpc.py:308
+#, python-format
+msgid "MSG_ID is %s"
+msgstr ""
+
+#: nova/rpc.py:356
+#, python-format
+msgid "response %s"
+msgstr ""
+
+#: nova/rpc.py:365
+#, python-format
+msgid "topic is %s"
+msgstr ""
+
+#: nova/rpc.py:366
+#, python-format
+msgid "message %s"
+msgstr ""
+
+#: nova/service.py:157
+#, python-format
+msgid "Starting %s node"
+msgstr ""
+
+#: nova/service.py:169
+msgid "Service killed that has no database entry"
+msgstr ""
+
+#: nova/service.py:190
+msgid "The service database object disappeared, Recreating it."
+msgstr ""
+
+#: nova/service.py:202
+msgid "Recovered model server connection!"
+msgstr ""
+
+#: nova/service.py:208
+msgid "model server went away"
+msgstr ""
+
+#: nova/service.py:217 nova/db/sqlalchemy/__init__.py:43
+#, python-format
+msgid "Data store %s is unreachable. Trying again in %d seconds."
+msgstr ""
+
+#: nova/service.py:232 nova/twistd.py:232
+#, python-format
+msgid "Serving %s"
+msgstr ""
+
+#: nova/service.py:234 nova/twistd.py:264
+msgid "Full set of FLAGS:"
+msgstr ""
+
+#: nova/twistd.py:211
+#, python-format
+msgid "pidfile %s does not exist. Daemon not running?\n"
+msgstr ""
+
+#: nova/twistd.py:268
+#, python-format
+msgid "Starting %s"
+msgstr ""
+
+#: nova/utils.py:53
+#, python-format
+msgid "Inner Exception: %s"
+msgstr ""
+
+#: nova/utils.py:54
+#, python-format
+msgid "Class %s cannot be found"
+msgstr ""
+
+#: nova/utils.py:113
+#, python-format
+msgid "Fetching %s"
+msgstr ""
+
+#: nova/utils.py:125
+#, python-format
+msgid "Running cmd (subprocess): %s"
+msgstr ""
+
+#: nova/utils.py:138
+#, python-format
+msgid "Result was %s"
+msgstr ""
+
+#: nova/utils.py:171
+#, python-format
+msgid "debug in callback: %s"
+msgstr ""
+
+#: nova/utils.py:176
+#, python-format
+msgid "Running %s"
+msgstr ""
+
+#: nova/utils.py:207
+#, python-format
+msgid "Couldn't get IP, using 127.0.0.1 %s"
+msgstr ""
+
+#: nova/utils.py:289
+#, python-format
+msgid "Invalid backend: %s"
+msgstr ""
+
+#: nova/utils.py:300
+#, python-format
+msgid "backend %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:133
+msgid "Too many failed authentications."
+msgstr ""
+
+#: nova/api/ec2/__init__.py:142
+#, python-format
+msgid ""
+"Access key %s has had %d failed authentications and will be locked out "
+"for %d minutes."
+msgstr ""
+
+#: nova/api/ec2/__init__.py:179 nova/objectstore/handler.py:140
+#, python-format
+msgid "Authentication Failure: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:190
+#, python-format
+msgid "Authenticated Request For %s:%s)"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:227
+#, python-format
+msgid "action: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:229
+#, python-format
+msgid "arg: %s\t\tval: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:301
+#, python-format
+msgid "Unauthorized request for controller=%s and action=%s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:339
+#, python-format
+msgid "NotFound raised: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:342
+#, python-format
+msgid "ApiError raised: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:349
+#, python-format
+msgid "Unexpected error raised: %s"
+msgstr ""
+
+#: nova/api/ec2/__init__.py:354
+msgid "An unknown error has occurred. Please try your request again."
+msgstr ""
+
+#: nova/api/ec2/admin.py:84
+#, python-format
+msgid "Creating new user: %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:92
+#, python-format
+msgid "Deleting user: %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:114
+#, python-format
+msgid "Adding role %s to user %s for project %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:117 nova/auth/manager.py:415
+#, python-format
+msgid "Adding sitewide role %s to user %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:122
+#, python-format
+msgid "Removing role %s from user %s for project %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:125 nova/auth/manager.py:441
+#, python-format
+msgid "Removing sitewide role %s from user %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:129 nova/api/ec2/admin.py:192
+msgid "operation must be add or remove"
+msgstr ""
+
+#: nova/api/ec2/admin.py:142
+#, python-format
+msgid "Getting x509 for user: %s on project: %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:159
+#, python-format
+msgid "Create project %s managed by %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:170
+#, python-format
+msgid "Delete project: %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:184 nova/auth/manager.py:533
+#, python-format
+msgid "Adding user %s to project %s"
+msgstr ""
+
+#: nova/api/ec2/admin.py:188
+#, python-format
+msgid "Removing user %s from project %s"
+msgstr ""
+
+#: nova/api/ec2/apirequest.py:95
+#, python-format
+msgid "Unsupported API request: controller = %s,action = %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:117
+#, python-format
+msgid "Generating root CA: %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:277
+#, python-format
+msgid "Create key pair %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:285
+#, python-format
+msgid "Delete key pair %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:357
+#, python-format
+msgid "%s is not a valid ipProtocol"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:361
+msgid "Invalid port range"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:392
+#, python-format
+msgid "Revoke security group ingress %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:401 nova/api/ec2/cloud.py:414
+msgid "No rule for the specified parameters."
+msgstr ""
+
+#: nova/api/ec2/cloud.py:421
+#, python-format
+msgid "Authorize security group ingress %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:432
+#, python-format
+msgid "This rule already exists in group %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:460
+#, python-format
+msgid "Create Security Group %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:463
+#, python-format
+msgid "group %s already exists"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:475
+#, python-format
+msgid "Delete security group %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:483 nova/compute/manager.py:452
+#, python-format
+msgid "Get console output for instance %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:543
+#, python-format
+msgid "Create volume of %s GB"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:567
+#, python-format
+msgid "Attach volume %s to instacne %s at %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:579
+#, python-format
+msgid "Detach volume %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:686
+msgid "Allocate address"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:691
+#, python-format
+msgid "Release address %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:696
+#, python-format
+msgid "Associate address %s to instance %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:703
+#, python-format
+msgid "Disassociate address %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:730
+msgid "Going to start terminating instances"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:738
+#, python-format
+msgid "Reboot instance %r"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:775
+#, python-format
+msgid "De-registering image %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:783
+#, python-format
+msgid "Registered image %s with id %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:789 nova/api/ec2/cloud.py:804
+#, python-format
+msgid "attribute not supported: %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:794
+#, python-format
+msgid "invalid id: %s"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:807
+msgid "user or group not specified"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:809
+msgid "only group \"all\" is supported"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:811
+msgid "operation_type must be add or remove"
+msgstr ""
+
+#: nova/api/ec2/cloud.py:812
+#, python-format
+msgid "Updating image %s publicity"
+msgstr ""
+
+#: nova/api/ec2/metadatarequesthandler.py:75
+#, python-format
+msgid "Failed to get metadata for ip: %s"
+msgstr ""
+
+#: nova/api/openstack/__init__.py:70
+#, python-format
+msgid "Caught error: %s"
+msgstr ""
+
+#: nova/api/openstack/__init__.py:86
+msgid "Including admin operations in API."
+msgstr ""
+
+#: nova/api/openstack/servers.py:184
+#, python-format
+msgid "Compute.api::lock %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:199
+#, python-format
+msgid "Compute.api::unlock %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:213
+#, python-format
+msgid "Compute.api::get_lock %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:224
+#, python-format
+msgid "Compute.api::pause %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:235
+#, python-format
+msgid "Compute.api::unpause %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:246
+#, python-format
+msgid "compute.api::suspend %s"
+msgstr ""
+
+#: nova/api/openstack/servers.py:257
+#, python-format
+msgid "compute.api::resume %s"
+msgstr ""
+
+#: nova/auth/dbdriver.py:84
+#, python-format
+msgid "User %s already exists"
+msgstr ""
+
+#: nova/auth/dbdriver.py:106 nova/auth/ldapdriver.py:207
+#, python-format
+msgid "Project can't be created because manager %s doesn't exist"
+msgstr ""
+
+#: nova/auth/dbdriver.py:135 nova/auth/ldapdriver.py:204
+#, python-format
+msgid "Project can't be created because project %s already exists"
+msgstr ""
+
+#: nova/auth/dbdriver.py:157 nova/auth/ldapdriver.py:241
+#, python-format
+msgid "Project can't be modified because manager %s doesn't exist"
+msgstr ""
+
+#: nova/auth/dbdriver.py:245
+#, python-format
+msgid "User \"%s\" not found"
+msgstr ""
+
+#: nova/auth/dbdriver.py:248
+#, python-format
+msgid "Project \"%s\" not found"
+msgstr ""
+
+#: nova/auth/fakeldap.py:33
+msgid "Attempted to instantiate singleton"
+msgstr ""
+
+#: nova/auth/ldapdriver.py:181
+#, python-format
+msgid "LDAP object for %s doesn't exist"
+msgstr ""
+
+#: nova/auth/ldapdriver.py:218
+#, python-format
+msgid "Project can't be created because user %s doesn't exist"
+msgstr ""
+
+#: nova/auth/ldapdriver.py:478
+#, python-format
+msgid "User %s is already a member of the group %s"
+msgstr ""
+
+#: nova/auth/ldapdriver.py:507
+#, python-format
+msgid ""
+"Attempted to remove the last member of a group. Deleting the group at %s "
+"instead."
+msgstr ""
+
+#: nova/auth/ldapdriver.py:528
+#, python-format
+msgid "Group at dn %s doesn't exist"
+msgstr ""
+
+#: nova/auth/manager.py:259
+#, python-format
+msgid "Looking up user: %r"
+msgstr ""
+
+#: nova/auth/manager.py:263
+#, python-format
+msgid "Failed authorization for access key %s"
+msgstr ""
+
+#: nova/auth/manager.py:264
+#, python-format
+msgid "No user found for access key %s"
+msgstr ""
+
+#: nova/auth/manager.py:270
+#, python-format
+msgid "Using project name = user name (%s)"
+msgstr ""
+
+#: nova/auth/manager.py:275
+#, python-format
+msgid "failed authorization: no project named %s (user=%s)"
+msgstr ""
+
+#: nova/auth/manager.py:277
+#, python-format
+msgid "No project called %s could be found"
+msgstr ""
+
+#: nova/auth/manager.py:281
+#, python-format
+msgid "Failed authorization: user %s not admin and not member of project %s"
+msgstr ""
+
+#: nova/auth/manager.py:283
+#, python-format
+msgid "User %s is not a member of project %s"
+msgstr ""
+
+#: nova/auth/manager.py:292 nova/auth/manager.py:303
+#, python-format
+msgid "Invalid signature for user %s"
+msgstr ""
+
+#: nova/auth/manager.py:293 nova/auth/manager.py:304
+msgid "Signature does not match"
+msgstr ""
+
+#: nova/auth/manager.py:374
+msgid "Must specify project"
+msgstr ""
+
+#: nova/auth/manager.py:408
+#, python-format
+msgid "The %s role can not be found"
+msgstr ""
+
+#: nova/auth/manager.py:410
+#, python-format
+msgid "The %s role is global only"
+msgstr ""
+
+#: nova/auth/manager.py:412
+#, python-format
+msgid "Adding role %s to user %s in project %s"
+msgstr ""
+
+#: nova/auth/manager.py:438
+#, python-format
+msgid "Removing role %s from user %s on project %s"
+msgstr ""
+
+#: nova/auth/manager.py:505
+#, python-format
+msgid "Created project %s with manager %s"
+msgstr ""
+
+#: nova/auth/manager.py:523
+#, python-format
+msgid "modifying project %s"
+msgstr ""
+
+#: nova/auth/manager.py:553
+#, python-format
+msgid "Remove user %s from project %s"
+msgstr ""
+
+#: nova/auth/manager.py:581
+#, python-format
+msgid "Deleting project %s"
+msgstr ""
+
+#: nova/auth/manager.py:637
+#, python-format
+msgid "Created user %s (admin: %r)"
+msgstr ""
+
+#: nova/auth/manager.py:645
+#, python-format
+msgid "Deleting user %s"
+msgstr ""
+
+#: nova/auth/manager.py:655
+#, python-format
+msgid "Access Key change for user %s"
+msgstr ""
+
+#: nova/auth/manager.py:657
+#, python-format
+msgid "Secret Key change for user %s"
+msgstr ""
+
+#: nova/auth/manager.py:659
+#, python-format
+msgid "Admin status set to %r for user %s"
+msgstr ""
+
+#: nova/auth/manager.py:708
+#, python-format
+msgid "No vpn data for project %s"
+msgstr ""
+
+#: nova/cloudpipe/pipelib.py:45
+msgid "Template for script to run on cloudpipe instance boot"
+msgstr ""
+
+#: nova/cloudpipe/pipelib.py:48
+msgid "Network to push into openvpn config"
+msgstr ""
+
+#: nova/cloudpipe/pipelib.py:51
+msgid "Netmask to push into openvpn config"
+msgstr ""
+
+#: nova/cloudpipe/pipelib.py:97
+#, python-format
+msgid "Launching VPN for %s"
+msgstr ""
+
+#: nova/compute/api.py:67
+#, python-format
+msgid "Instance %d was not found in get_network_topic"
+msgstr ""
+
+#: nova/compute/api.py:73
+#, python-format
+msgid "Instance %d has no host"
+msgstr ""
+
+#: nova/compute/api.py:92
+#, python-format
+msgid "Quota exceeeded for %s, tried to run %s instances"
+msgstr ""
+
+#: nova/compute/api.py:94
+#, python-format
+msgid "Instance quota exceeded. You can only run %s more instances of this type."
+msgstr ""
+
+#: nova/compute/api.py:109
+msgid "Creating a raw instance"
+msgstr ""
+
+#: nova/compute/api.py:156
+#, python-format
+msgid "Going to run %s instances..."
+msgstr ""
+
+#: nova/compute/api.py:180
+#, python-format
+msgid "Casting to scheduler for %s/%s's instance %s"
+msgstr ""
+
+#: nova/compute/api.py:279
+#, python-format
+msgid "Going to try and terminate %s"
+msgstr ""
+
+#: nova/compute/api.py:283
+#, python-format
+msgid "Instance %d was not found during terminate"
+msgstr ""
+
+#: nova/compute/api.py:288
+#, python-format
+msgid "Instance %d is already being terminated"
+msgstr ""
+
+#: nova/compute/api.py:450
+#, python-format
+msgid "Invalid device specified: %s. Example device: /dev/vdb"
+msgstr ""
+
+#: nova/compute/api.py:465
+msgid "Volume isn't attached to anything!"
+msgstr ""
+
+#: nova/compute/disk.py:71
+#, python-format
+msgid "Input partition size not evenly divisible by sector size: %d / %d"
+msgstr ""
+
+#: nova/compute/disk.py:75
+#, python-format
+msgid "Bytes for local storage not evenly divisible by sector size: %d / %d"
+msgstr ""
+
+#: nova/compute/disk.py:128
+#, python-format
+msgid "Could not attach image to loopback: %s"
+msgstr ""
+
+#: nova/compute/disk.py:136
+#, python-format
+msgid "Failed to load partition: %s"
+msgstr ""
+
+#: nova/compute/disk.py:158
+#, python-format
+msgid "Failed to mount filesystem: %s"
+msgstr ""
+
+#: nova/compute/instance_types.py:41
+#, python-format
+msgid "Unknown instance type: %s"
+msgstr ""
+
+#: nova/compute/manager.py:69
+#, python-format
+msgid "check_instance_lock: decorating: |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:71
+#, python-format
+msgid "check_instance_lock: arguments: |%s| |%s| |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:75
+#, python-format
+msgid "check_instance_lock: locked: |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:77
+#, python-format
+msgid "check_instance_lock: admin: |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:82
+#, python-format
+msgid "check_instance_lock: executing: |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:86
+#, python-format
+msgid "check_instance_lock: not executing |%s|"
+msgstr ""
+
+#: nova/compute/manager.py:157
+msgid "Instance has already been created"
+msgstr ""
+
+#: nova/compute/manager.py:158
+#, python-format
+msgid "instance %s: starting..."
+msgstr ""
+
+#: nova/compute/manager.py:197
+#, python-format
+msgid "instance %s: Failed to spawn"
+msgstr ""
+
+#: nova/compute/manager.py:211 nova/tests/test_cloud.py:228
+#, python-format
+msgid "Terminating instance %s"
+msgstr ""
+
+#: nova/compute/manager.py:217
+#, python-format
+msgid "Disassociating address %s"
+msgstr ""
+
+#: nova/compute/manager.py:230
+#, python-format
+msgid "Deallocating address %s"
+msgstr ""
+
+#: nova/compute/manager.py:243
+#, python-format
+msgid "trying to destroy already destroyed instance: %s"
+msgstr ""
+
+#: nova/compute/manager.py:257
+#, python-format
+msgid "Rebooting instance %s"
+msgstr ""
+
+#: nova/compute/manager.py:260
+#, python-format
+msgid "trying to reboot a non-running instance: %s (state: %s excepted: %s)"
+msgstr ""
+
+#: nova/compute/manager.py:286
+#, python-format
+msgid "instance %s: snapshotting"
+msgstr ""
+
+#: nova/compute/manager.py:289
+#, python-format
+msgid "trying to snapshot a non-running instance: %s (state: %s excepted: %s)"
+msgstr ""
+
+#: nova/compute/manager.py:301
+#, python-format
+msgid "instance %s: rescuing"
+msgstr ""
+
+#: nova/compute/manager.py:316
+#, python-format
+msgid "instance %s: unrescuing"
+msgstr ""
+
+#: nova/compute/manager.py:335
+#, python-format
+msgid "instance %s: pausing"
+msgstr ""
+
+#: nova/compute/manager.py:352
+#, python-format
+msgid "instance %s: unpausing"
+msgstr ""
+
+#: nova/compute/manager.py:369
+#, python-format
+msgid "instance %s: retrieving diagnostics"
+msgstr ""
+
+#: nova/compute/manager.py:382
+#, python-format
+msgid "instance %s: suspending"
+msgstr ""
+
+#: nova/compute/manager.py:401
+#, python-format
+msgid "instance %s: resuming"
+msgstr ""
+
+#: nova/compute/manager.py:420
+#, python-format
+msgid "instance %s: locking"
+msgstr ""
+
+#: nova/compute/manager.py:432
+#, python-format
+msgid "instance %s: unlocking"
+msgstr ""
+
+#: nova/compute/manager.py:442
+#, python-format
+msgid "instance %s: getting locked state"
+msgstr ""
+
+#: nova/compute/manager.py:462
+#, python-format
+msgid "instance %s: attaching volume %s to %s"
+msgstr ""
+
+#: nova/compute/manager.py:478
+#, python-format
+msgid "instance %s: attach failed %s, removing"
+msgstr ""
+
+#: nova/compute/manager.py:493
+#, python-format
+msgid "Detach volume %s from mountpoint %s on instance %s"
+msgstr ""
+
+#: nova/compute/manager.py:497
+#, python-format
+msgid "Detaching volume from unknown instance %s"
+msgstr ""
+
+#: nova/compute/monitor.py:259
+#, python-format
+msgid "updating %s..."
+msgstr ""
+
+#: nova/compute/monitor.py:289
+msgid "unexpected error during update"
+msgstr ""
+
+#: nova/compute/monitor.py:355
+#, python-format
+msgid "Cannot get blockstats for \"%s\" on \"%s\""
+msgstr ""
+
+#: nova/compute/monitor.py:377
+#, python-format
+msgid "Cannot get ifstats for \"%s\" on \"%s\""
+msgstr ""
+
+#: nova/compute/monitor.py:412
+msgid "unexpected exception getting connection"
+msgstr ""
+
+#: nova/compute/monitor.py:427
+#, python-format
+msgid "Found instance: %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:43
+msgid "Use of empty request context is deprecated"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:132
+#, python-format
+msgid "No service for id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:229
+#, python-format
+msgid "No service for %s, %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:574
+#, python-format
+msgid "No floating ip for address %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:668
+#, python-format
+msgid "No instance for id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:758 nova/virt/libvirt_conn.py:598
+#: nova/virt/xenapi/volumeops.py:48 nova/virt/xenapi/volumeops.py:103
+#, python-format
+msgid "Instance %s not found"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:891
+#, python-format
+msgid "no keypair for user %s, name %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1006 nova/db/sqlalchemy/api.py:1064
+#, python-format
+msgid "No network for id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1036
+#, python-format
+msgid "No network for bridge %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1050
+#, python-format
+msgid "No network for instance %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1180
+#, python-format
+msgid "Token %s does not exist"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1205
+#, python-format
+msgid "No quota for project_id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1356
+#, python-format
+msgid "No volume for id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1401
+#, python-format
+msgid "Volume %s not found"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1413
+#, python-format
+msgid "No export device found for volume %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1426
+#, python-format
+msgid "No target id found for volume %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1471
+#, python-format
+msgid "No security group with id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1488
+#, python-format
+msgid "No security group named %s for project: %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1576
+#, python-format
+msgid "No secuity group rule with id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1650
+#, python-format
+msgid "No user for id %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1666
+#, python-format
+msgid "No user for access key %s"
+msgstr ""
+
+#: nova/db/sqlalchemy/api.py:1728
+#, python-format
+msgid "No project with id %s"
+msgstr ""
+
+#: nova/image/glance.py:78
+#, python-format
+msgid "Parallax returned HTTP error %d from request for /images"
+msgstr ""
+
+#: nova/image/glance.py:97
+#, python-format
+msgid "Parallax returned HTTP error %d from request for /images/detail"
+msgstr ""
+
+#: nova/image/s3.py:82
+#, python-format
+msgid "Image %s could not be found"
+msgstr ""
+
+#: nova/network/api.py:39
+#, python-format
+msgid "Quota exceeeded for %s, tried to allocate address"
+msgstr ""
+
+#: nova/network/api.py:42
+msgid "Address quota exceeded. You cannot allocate any more addresses"
+msgstr ""
+
+#: nova/network/linux_net.py:176
+#, python-format
+msgid "Starting VLAN inteface %s"
+msgstr ""
+
+#: nova/network/linux_net.py:186
+#, python-format
+msgid "Starting Bridge interface for %s"
+msgstr ""
+
+#: nova/network/linux_net.py:254
+#, python-format
+msgid "Hupping dnsmasq threw %s"
+msgstr ""
+
+#: nova/network/linux_net.py:256
+#, python-format
+msgid "Pid %d is stale, relaunching dnsmasq"
+msgstr ""
+
+#: nova/network/linux_net.py:334
+#, python-format
+msgid "Killing dnsmasq threw %s"
+msgstr ""
+
+#: nova/network/manager.py:135
+msgid "setting network host"
+msgstr ""
+
+#: nova/network/manager.py:190
+#, python-format
+msgid "Leasing IP %s"
+msgstr ""
+
+#: nova/network/manager.py:194
+#, python-format
+msgid "IP %s leased that isn't associated"
+msgstr ""
+
+#: nova/network/manager.py:197
+#, python-format
+msgid "IP %s leased to bad mac %s vs %s"
+msgstr ""
+
+#: nova/network/manager.py:205
+#, python-format
+msgid "IP %s leased that was already deallocated"
+msgstr ""
+
+#: nova/network/manager.py:214
+#, python-format
+msgid "IP %s released that isn't associated"
+msgstr ""
+
+#: nova/network/manager.py:217
+#, python-format
+msgid "IP %s released from bad mac %s vs %s"
+msgstr ""
+
+#: nova/network/manager.py:220
+#, python-format
+msgid "IP %s released that was not leased"
+msgstr ""
+
+#: nova/network/manager.py:442
+#, python-format
+msgid "Dissassociated %s stale fixed ip(s)"
+msgstr ""
+
+#: nova/objectstore/handler.py:106
+#, python-format
+msgid "Unknown S3 value type %r"
+msgstr ""
+
+#: nova/objectstore/handler.py:137
+msgid "Authenticated request"
+msgstr ""
+
+#: nova/objectstore/handler.py:182
+msgid "List of buckets requested"
+msgstr ""
+
+#: nova/objectstore/handler.py:209
+#, python-format
+msgid "List keys for bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:217
+#, python-format
+msgid "Unauthorized attempt to access bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:235
+#, python-format
+msgid "Creating bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:245
+#, python-format
+msgid "Deleting bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:249
+#, python-format
+msgid "Unauthorized attempt to delete bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:271
+#, python-format
+msgid "Getting object: %s / %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:274
+#, python-format
+msgid "Unauthorized attempt to get object %s from bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:292
+#, python-format
+msgid "Putting object: %s / %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:295
+#, python-format
+msgid "Unauthorized attempt to upload object %s to bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:314
+#, python-format
+msgid "Deleting object: %s / %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:393
+#, python-format
+msgid "Not authorized to upload image: invalid directory %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:401
+#, python-format
+msgid "Not authorized to upload image: unauthorized bucket %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:406
+#, python-format
+msgid "Starting image upload: %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:420
+#, python-format
+msgid "Not authorized to update attributes of image %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:428
+#, python-format
+msgid "Toggling publicity flag of image %s %r"
+msgstr ""
+
+#: nova/objectstore/handler.py:433
+#, python-format
+msgid "Updating user fields on image %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:447
+#, python-format
+msgid "Unauthorized attempt to delete image %s"
+msgstr ""
+
+#: nova/objectstore/handler.py:452
+#, python-format
+msgid "Deleted image: %s"
+msgstr ""
+
+#: nova/scheduler/chance.py:37 nova/scheduler/simple.py:73
+#: nova/scheduler/simple.py:106 nova/scheduler/simple.py:118
+msgid "No hosts found"
+msgstr ""
+
+#: nova/scheduler/driver.py:66
+msgid "Must implement a fallback schedule"
+msgstr ""
+
+#: nova/scheduler/manager.py:69
+#, python-format
+msgid "Casting to %s %s for %s"
+msgstr ""
+
+#: nova/scheduler/simple.py:63
+msgid "All hosts have too many cores"
+msgstr ""
+
+#: nova/scheduler/simple.py:95
+msgid "All hosts have too many gigabytes"
+msgstr ""
+
+#: nova/scheduler/simple.py:115
+msgid "All hosts have too many networks"
+msgstr ""
+
+#: nova/tests/test_cloud.py:198
+msgid "Can't test instances without a real virtual env."
+msgstr ""
+
+#: nova/tests/test_cloud.py:210
+#, python-format
+msgid "Need to watch instance %s until it's running..."
+msgstr ""
+
+#: nova/tests/test_compute.py:104
+#, python-format
+msgid "Running instances: %s"
+msgstr ""
+
+#: nova/tests/test_compute.py:110
+#, python-format
+msgid "After terminating instances: %s"
+msgstr ""
+
+#: nova/tests/test_rpc.py:89
+#, python-format
+msgid "Nested received %s, %s"
+msgstr ""
+
+#: nova/tests/test_rpc.py:94
+#, python-format
+msgid "Nested return %s"
+msgstr ""
+
+#: nova/tests/test_rpc.py:119 nova/tests/test_rpc.py:125
+#, python-format
+msgid "Received %s"
+msgstr ""
+
+#: nova/tests/test_volume.py:162
+#, python-format
+msgid "Target %s allocated"
+msgstr ""
+
+#: nova/virt/connection.py:73
+msgid "Failed to open connection to the hypervisor"
+msgstr ""
+
+#: nova/virt/fake.py:210
+#, python-format
+msgid "Instance %s Not Found"
+msgstr ""
+
+#: nova/virt/hyperv.py:118
+msgid "In init host"
+msgstr ""
+
+#: nova/virt/hyperv.py:131
+#, python-format
+msgid "Attempt to create duplicate vm %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:148
+#, python-format
+msgid "Starting VM %s "
+msgstr ""
+
+#: nova/virt/hyperv.py:150
+#, python-format
+msgid "Started VM %s "
+msgstr ""
+
+#: nova/virt/hyperv.py:152
+#, python-format
+msgid "spawn vm failed: %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:169
+#, python-format
+msgid "Failed to create VM %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:171 nova/virt/xenapi/vm_utils.py:125
+#, python-format
+msgid "Created VM %s..."
+msgstr ""
+
+#: nova/virt/hyperv.py:188
+#, python-format
+msgid "Set memory for vm %s..."
+msgstr ""
+
+#: nova/virt/hyperv.py:198
+#, python-format
+msgid "Set vcpus for vm %s..."
+msgstr ""
+
+#: nova/virt/hyperv.py:202
+#, python-format
+msgid "Creating disk for %s by attaching disk file %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:227
+#, python-format
+msgid "Failed to add diskdrive to VM %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:230
+#, python-format
+msgid "New disk drive path is %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:247
+#, python-format
+msgid "Failed to add vhd file to VM %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:249
+#, python-format
+msgid "Created disk for %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:253
+#, python-format
+msgid "Creating nic for %s "
+msgstr ""
+
+#: nova/virt/hyperv.py:272
+msgid "Failed creating a port on the external vswitch"
+msgstr ""
+
+#: nova/virt/hyperv.py:273
+#, python-format
+msgid "Failed creating port for %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:275
+#, python-format
+msgid "Created switch port %s on switch %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:285
+#, python-format
+msgid "Failed to add nic to VM %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:287
+#, python-format
+msgid "Created nic for %s "
+msgstr ""
+
+#: nova/virt/hyperv.py:320
+#, python-format
+msgid "WMI job failed: %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:322
+#, python-format
+msgid "WMI job succeeded: %s, Elapsed=%s "
+msgstr ""
+
+#: nova/virt/hyperv.py:358
+#, python-format
+msgid "Got request to destroy vm %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:383
+#, python-format
+msgid "Failed to destroy vm %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:389
+#, python-format
+msgid "Del: disk %s vm %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:405
+#, python-format
+msgid ""
+"Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, "
+"cpu_time=%s"
+msgstr ""
+
+#: nova/virt/hyperv.py:424 nova/virt/xenapi/vm_utils.py:301
+#, python-format
+msgid "duplicate name found: %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:444
+#, python-format
+msgid "Successfully changed vm state of %s to %s"
+msgstr ""
+
+#: nova/virt/hyperv.py:447 nova/virt/hyperv.py:449
+#, python-format
+msgid "Failed to change vm state of %s to %s"
+msgstr ""
+
+#: nova/virt/images.py:70
+#, python-format
+msgid "Finished retreving %s -- placed in %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:144
+#, python-format
+msgid "Connecting to libvirt: %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:157
+msgid "Connection to libvirt broke"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:229
+#, python-format
+msgid "instance %s: deleting instance files %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:271
+#, python-format
+msgid "No disk at %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:278
+msgid "Instance snapshotting is not supported for libvirtat this time"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:294
+#, python-format
+msgid "instance %s: rebooted"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:297
+#, python-format
+msgid "_wait_for_reboot failed: %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:340
+#, python-format
+msgid "instance %s: rescued"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:343
+#, python-format
+msgid "_wait_for_rescue failed: %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:370
+#, python-format
+msgid "instance %s: is running"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:381
+#, python-format
+msgid "instance %s: booted"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:384 nova/virt/xenapi/vmops.py:116
+#, python-format
+msgid "instance %s: failed to boot"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:395
+#, python-format
+msgid "virsh said: %r"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:399
+msgid "cool, it's a device"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:407
+#, python-format
+msgid "data: %r, fpath: %r"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:415
+#, python-format
+msgid "Contents of file %s: %r"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:449
+#, python-format
+msgid "instance %s: Creating image"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:505
+#, python-format
+msgid "instance %s: injecting key into image %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:508
+#, python-format
+msgid "instance %s: injecting net into image %s"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:516
+#, python-format
+msgid "instance %s: ignoring error injecting data into image %s (%s)"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:544 nova/virt/libvirt_conn.py:547
+#, python-format
+msgid "instance %s: starting toXML method"
+msgstr ""
+
+#: nova/virt/libvirt_conn.py:589
+#, python-format
+msgid "instance %s: finished toXML method"
+msgstr ""
+
+#: nova/virt/xenapi_conn.py:113
+msgid ""
+"Must specify xenapi_connection_url, xenapi_connection_username "
+"(optionally), and xenapi_connection_password to use "
+"connection_type=xenapi"
+msgstr ""
+
+#: nova/virt/xenapi_conn.py:263
+#, python-format
+msgid "Task [%s] %s status: success %s"
+msgstr ""
+
+#: nova/virt/xenapi_conn.py:271
+#, python-format
+msgid "Task [%s] %s status: %s %s"
+msgstr ""
+
+#: nova/virt/xenapi_conn.py:287 nova/virt/xenapi_conn.py:300
+#, python-format
+msgid "Got exception: %s"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:72
+#, python-format
+msgid "%s: _db_content => %s"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:247 nova/virt/xenapi/fake.py:338
+#: nova/virt/xenapi/fake.py:356 nova/virt/xenapi/fake.py:404
+msgid "Raising NotImplemented"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:249
+#, python-format
+msgid "xenapi.fake does not have an implementation for %s"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:283
+#, python-format
+msgid "Calling %s %s"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:288
+#, python-format
+msgid "Calling getter %s"
+msgstr ""
+
+#: nova/virt/xenapi/fake.py:340
+#, python-format
+msgid ""
+"xenapi.fake does not have an implementation for %s or it has been called "
+"with the wrong number of arguments"
+msgstr ""
+
+#: nova/virt/xenapi/network_utils.py:40
+#, python-format
+msgid "Found non-unique network for bridge %s"
+msgstr ""
+
+#: nova/virt/xenapi/network_utils.py:43
+#, python-format
+msgid "Found no network for bridge %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:127
+#, python-format
+msgid "Created VM %s as %s."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:147
+#, python-format
+msgid "Creating VBD for VM %s, VDI %s ... "
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:149
+#, python-format
+msgid "Created VBD %s for VM %s, VDI %s."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:165
+#, python-format
+msgid "VBD not found in instance %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:175
+#, python-format
+msgid "Unable to unplug VBD %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:187
+#, python-format
+msgid "Unable to destroy VBD %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:202
+#, python-format
+msgid "Creating VIF for VM %s, network %s."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:205
+#, python-format
+msgid "Created VIF %s for VM %s, network %s."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:216
+#, python-format
+msgid "Snapshotting VM %s with label '%s'..."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:229
+#, python-format
+msgid "Created snapshot %s from VM %s."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:243
+#, python-format
+msgid "Asking xapi to upload %s as '%s'"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:261
+#, python-format
+msgid "Asking xapi to fetch %s as %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:279
+#, python-format
+msgid "Looking up vdi %s for PV kernel"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:290
+#, python-format
+msgid "PV Kernel in VDI:%d"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:318
+#, python-format
+msgid "VDI %s is still available"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:331
+#, python-format
+msgid "(VM_UTILS) xenserver vm state -> |%s|"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:333
+#, python-format
+msgid "(VM_UTILS) xenapi power_state -> |%s|"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:390
+#, python-format
+msgid "VHD %s has parent %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:407
+#, python-format
+msgid "Re-scanning SR %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:431
+#, python-format
+msgid "Parent %s doesn't match original parent %s, waiting for coalesce..."
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:448
+#, python-format
+msgid "No VDIs found for VM %s"
+msgstr ""
+
+#: nova/virt/xenapi/vm_utils.py:452
+#, python-format
+msgid "Unexpected number of VDIs (%s) found for VM %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:62
+#, python-format
+msgid "Attempted to create non-unique name %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:99
+#, python-format
+msgid "Starting VM %s..."
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:101
+#, python-format
+msgid "Spawning VM %s created %s."
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:112
+#, python-format
+msgid "Instance %s: booted"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:137
+#, python-format
+msgid "Instance not present %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:166
+#, python-format
+msgid "Starting snapshot for VM %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:174
+#, python-format
+msgid "Unable to Snapshot %s: %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:184
+#, python-format
+msgid "Finished snapshot and upload for VM %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:252
+#, python-format
+msgid "suspend: instance not present %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:262
+#, python-format
+msgid "resume: instance not present %s"
+msgstr ""
+
+#: nova/virt/xenapi/vmops.py:271
+#, python-format
+msgid "Instance not found %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:57
+#, python-format
+msgid "Introducing %s..."
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:74
+#, python-format
+msgid "Introduced %s as %s."
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:78
+msgid "Unable to create Storage Repository"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:90
+#, python-format
+msgid "Unable to find SR from VBD %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:96
+#, python-format
+msgid "Forgetting SR %s ... "
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:101
+#, python-format
+msgid "Ignoring exception %s when getting PBDs for %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:107
+#, python-format
+msgid "Ignoring exception %s when unplugging PBD %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:111
+#, python-format
+msgid "Forgetting SR %s done."
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:113
+#, python-format
+msgid "Ignoring exception %s when forgetting SR %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:123
+#, python-format
+msgid "Unable to introduce VDI on SR %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:128
+#, python-format
+msgid "Unable to get record of VDI %s on"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:146
+#, python-format
+msgid "Unable to introduce VDI for SR %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:175
+#, python-format
+msgid "Unable to obtain target information %s, %s"
+msgstr ""
+
+#: nova/virt/xenapi/volume_utils.py:197
+#, python-format
+msgid "Mountpoint cannot be translated: %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:51
+#, python-format
+msgid "Attach_volume: %s, %s, %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:69
+#, python-format
+msgid "Unable to create VDI on SR %s for instance %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:81
+#, python-format
+msgid "Unable to use SR %s for instance %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:93
+#, python-format
+msgid "Unable to attach volume to instance %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:95
+#, python-format
+msgid "Mountpoint %s attached to instance %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:106
+#, python-format
+msgid "Detach_volume: %s, %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:113
+#, python-format
+msgid "Unable to locate volume %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:121
+#, python-format
+msgid "Unable to detach volume %s"
+msgstr ""
+
+#: nova/virt/xenapi/volumeops.py:128
+#, python-format
+msgid "Mountpoint %s detached from instance %s"
+msgstr ""
+
+#: nova/volume/api.py:44
+#, python-format
+msgid "Quota exceeeded for %s, tried to create %sG volume"
+msgstr ""
+
+#: nova/volume/api.py:46
+#, python-format
+msgid "Volume quota exceeded. You cannot create a volume of size %s"
+msgstr ""
+
+#: nova/volume/api.py:70 nova/volume/api.py:95
+msgid "Volume status must be available"
+msgstr ""
+
+#: nova/volume/api.py:97
+msgid "Volume is already attached"
+msgstr ""
+
+#: nova/volume/api.py:103
+msgid "Volume is already detached"
+msgstr ""
+
+#: nova/volume/driver.py:76
+#, python-format
+msgid "Recovering from a failed execute. Try number %s"
+msgstr ""
+
+#: nova/volume/driver.py:85
+#, python-format
+msgid "volume group %s doesn't exist"
+msgstr ""
+
+#: nova/volume/driver.py:210
+#, python-format
+msgid "FAKE AOE: %s"
+msgstr ""
+
+#: nova/volume/driver.py:315
+#, python-format
+msgid "FAKE ISCSI: %s"
+msgstr ""
+
+#: nova/volume/manager.py:85
+#, python-format
+msgid "Re-exporting %s volumes"
+msgstr ""
+
+#: nova/volume/manager.py:93
+#, python-format
+msgid "volume %s: creating"
+msgstr ""
+
+#: nova/volume/manager.py:102
+#, python-format
+msgid "volume %s: creating lv of size %sG"
+msgstr ""
+
+#: nova/volume/manager.py:106
+#, python-format
+msgid "volume %s: creating export"
+msgstr ""
+
+#: nova/volume/manager.py:113
+#, python-format
+msgid "volume %s: created successfully"
+msgstr ""
+
+#: nova/volume/manager.py:121
+msgid "Volume is still attached"
+msgstr ""
+
+#: nova/volume/manager.py:123
+msgid "Volume is not local to this node"
+msgstr ""
+
+#: nova/volume/manager.py:124
+#, python-format
+msgid "volume %s: removing export"
+msgstr ""
+
+#: nova/volume/manager.py:126
+#, python-format
+msgid "volume %s: deleting"
+msgstr ""
+
+#: nova/volume/manager.py:129
+#, python-format
+msgid "volume %s: deleted successfully"
+msgstr ""
+
diff --git a/nova/api/__init__.py b/nova/api/__init__.py
index 26fed847b..803470570 100644
--- a/nova/api/__init__.py
+++ b/nova/api/__init__.py
@@ -24,7 +24,6 @@ Root WSGI middleware for all API controllers.
:ec2api_subdomain: subdomain running the EC2 API (default: ec2)
"""
-import logging
import routes
import webob.dec
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index aa3bfaeb4..2fa1f636c 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -20,7 +20,7 @@ Starting point for routing EC2 requests.
"""
-import logging
+import datetime
import routes
import webob
import webob.dec
@@ -29,6 +29,7 @@ import webob.exc
from nova import context
from nova import exception
from nova import flags
+from nova import log as logging
from nova import wsgi
from nova.api.ec2 import apirequest
from nova.api.ec2 import admin
@@ -37,6 +38,7 @@ from nova.auth import manager
FLAGS = flags.FLAGS
+LOG = logging.getLogger("nova.api")
flags.DEFINE_boolean('use_forwarded_for', False,
'Treat X-Forwarded-For as the canonical remote address. '
'Only enable this if you have a sanitizing proxy.')
@@ -52,10 +54,6 @@ flags.DEFINE_list('lockout_memcached_servers', None,
'Memcached servers or None for in process cache.')
-_log = logging.getLogger("api")
-_log.setLevel(logging.DEBUG)
-
-
class API(wsgi.Middleware):
"""Routing for all EC2 API requests."""
@@ -64,6 +62,40 @@ class API(wsgi.Middleware):
if FLAGS.use_lockout:
self.application = Lockout(self.application)
+ @webob.dec.wsgify
+ def __call__(self, req):
+ rv = req.get_response(self.application)
+ self.log_request_completion(rv, req)
+ return rv
+
+ def log_request_completion(self, response, request):
+ controller = request.environ.get('ec2.controller', None)
+ if controller:
+ controller = controller.__class__.__name__
+ action = request.environ.get('ec2.action', None)
+ ctxt = request.environ.get('ec2.context', None)
+ seconds = 'X'
+ microseconds = 'X'
+ if ctxt:
+ delta = datetime.datetime.utcnow() - \
+ ctxt.timestamp
+ seconds = delta.seconds
+ microseconds = delta.microseconds
+ LOG.info(
+ "%s.%ss %s %s %s %s:%s %s [%s] %s %s",
+ seconds,
+ microseconds,
+ request.remote_addr,
+ request.method,
+ request.path_info,
+ controller,
+ action,
+ response.status_int,
+ request.user_agent,
+ request.content_type,
+ response.content_type,
+ context=ctxt)
+
class Lockout(wsgi.Middleware):
"""Lockout for x minutes on y failed auths in a z minute period.
@@ -98,7 +130,7 @@ class Lockout(wsgi.Middleware):
failures_key = "authfailures-%s" % access_key
failures = int(self.mc.get(failures_key) or 0)
if failures >= FLAGS.lockout_attempts:
- detail = "Too many failed authentications."
+ detail = _("Too many failed authentications.")
raise webob.exc.HTTPForbidden(detail=detail)
res = req.get_response(self.application)
if res.status_int == 403:
@@ -107,9 +139,9 @@ class Lockout(wsgi.Middleware):
# NOTE(vish): To use incr, failures has to be a string.
self.mc.set(failures_key, '1', time=FLAGS.lockout_window * 60)
elif failures >= FLAGS.lockout_attempts:
- _log.warn('Access key %s has had %d failed authentications'
- ' and will be locked out for %d minutes.' %
- (access_key, failures, FLAGS.lockout_minutes))
+ LOG.warn(_('Access key %s has had %d failed authentications'
+ ' and will be locked out for %d minutes.'),
+ access_key, failures, FLAGS.lockout_minutes)
self.mc.set(failures_key, str(failures),
time=FLAGS.lockout_minutes * 60)
return res
@@ -142,8 +174,9 @@ class Authenticate(wsgi.Middleware):
req.method,
req.host,
req.path)
- except exception.Error, ex:
- logging.debug(_("Authentication Failure: %s") % ex)
+ # Be explicit for what exceptions are 403, the rest bubble as 500
+ except (exception.NotFound, exception.NotAuthorized) as ex:
+ LOG.audit(_("Authentication Failure: %s"), str(ex))
raise webob.exc.HTTPForbidden()
# Authenticated!
@@ -154,6 +187,8 @@ class Authenticate(wsgi.Middleware):
project=project,
remote_address=remote_address)
req.environ['ec2.context'] = ctxt
+ LOG.audit(_('Authenticated Request For %s:%s)'), user.name,
+ project.name, context=req.environ['ec2.context'])
return self.application
@@ -189,9 +224,9 @@ class Router(wsgi.Middleware):
except:
raise webob.exc.HTTPBadRequest()
- _log.debug(_('action: %s') % action)
+ LOG.debug(_('action: %s'), action)
for key, value in args.items():
- _log.debug(_('arg: %s\t\tval: %s') % (key, value))
+ LOG.debug(_('arg: %s\t\tval: %s'), key, value)
# Success!
req.environ['ec2.controller'] = controller
@@ -263,6 +298,9 @@ class Authorizer(wsgi.Middleware):
if self._matches_any_role(context, allowed_roles):
return self.application
else:
+ LOG.audit(_("Unauthorized request for controller=%s "
+ "and action=%s"), controller_name, action,
+ context=context)
raise webob.exc.HTTPUnauthorized()
def _matches_any_role(self, context, roles):
@@ -297,15 +335,24 @@ class Executor(wsgi.Application):
result = None
try:
result = api_request.send(context, **args)
+ except exception.NotFound as ex:
+ LOG.info(_('NotFound raised: %s'), str(ex), context=context)
+ return self._error(req, context, type(ex).__name__, str(ex))
except exception.ApiError as ex:
-
+ LOG.exception(_('ApiError raised: %s'), str(ex), context=context)
if ex.code:
- return self._error(req, ex.code, ex.message)
+ return self._error(req, context, ex.code, str(ex))
else:
- return self._error(req, type(ex).__name__, ex.message)
- # TODO(vish): do something more useful with unknown exceptions
+ return self._error(req, context, type(ex).__name__, str(ex))
except Exception as ex:
- return self._error(req, type(ex).__name__, str(ex))
+ extra = {'environment': req.environ}
+ LOG.exception(_('Unexpected error raised: %s'), str(ex),
+ extra=extra, context=context)
+ return self._error(req,
+ context,
+ 'UnknownError',
+ _('An unknown error has occurred. '
+ 'Please try your request again.'))
else:
resp = webob.Response()
resp.status = 200
@@ -313,15 +360,16 @@ class Executor(wsgi.Application):
resp.body = str(result)
return resp
- def _error(self, req, code, message):
- logging.error("%s: %s", code, message)
+ def _error(self, req, context, code, message):
+ LOG.error("%s: %s", code, message, context=context)
resp = webob.Response()
resp.status = 400
resp.headers['Content-Type'] = 'text/xml'
resp.body = str('<?xml version="1.0"?>\n'
- '<Response><Errors><Error><Code>%s</Code>'
- '<Message>%s</Message></Error></Errors>'
- '<RequestID>?</RequestID></Response>' % (code, message))
+ '<Response><Errors><Error><Code>%s</Code>'
+ '<Message>%s</Message></Error></Errors>'
+ '<RequestID>%s</RequestID></Response>' %
+ (code, message, context.request_id))
return resp
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index fac01369e..758b612e8 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -24,9 +24,13 @@ import base64
from nova import db
from nova import exception
+from nova import log as logging
from nova.auth import manager
+LOG = logging.getLogger('nova.api.ec2.admin')
+
+
def user_dict(user, base64_file=None):
"""Convert the user object to a result dict"""
if user:
@@ -75,17 +79,18 @@ class AdminController(object):
return {'userSet':
[user_dict(u) for u in manager.AuthManager().get_users()]}
- def register_user(self, _context, name, **_kwargs):
+ def register_user(self, context, name, **_kwargs):
"""Creates a new user, and returns generated credentials."""
+ LOG.audit(_("Creating new user: %s"), name, context=context)
return user_dict(manager.AuthManager().create_user(name))
- def deregister_user(self, _context, name, **_kwargs):
+ def deregister_user(self, context, name, **_kwargs):
"""Deletes a single user (NOT undoable.)
Should throw an exception if the user has instances,
volumes, or buckets remaining.
"""
+ LOG.audit(_("Deleting user: %s"), name, context=context)
manager.AuthManager().delete_user(name)
-
return True
def describe_roles(self, context, project_roles=True, **kwargs):
@@ -105,15 +110,27 @@ class AdminController(object):
operation='add', **kwargs):
"""Add or remove a role for a user and project."""
if operation == 'add':
+ if project:
+ LOG.audit(_("Adding role %s to user %s for project %s"), role,
+ user, project, context=context)
+ else:
+ LOG.audit(_("Adding sitewide role %s to user %s"), role, user,
+ context=context)
manager.AuthManager().add_role(user, role, project)
elif operation == 'remove':
+ if project:
+ LOG.audit(_("Removing role %s from user %s for project %s"),
+ role, user, project, context=context)
+ else:
+ LOG.audit(_("Removing sitewide role %s from user %s"), role,
+ user, context=context)
manager.AuthManager().remove_role(user, role, project)
else:
- raise exception.ApiError('operation must be add or remove')
+ raise exception.ApiError(_('operation must be add or remove'))
return True
- def generate_x509_for_user(self, _context, name, project=None, **kwargs):
+ def generate_x509_for_user(self, context, name, project=None, **kwargs):
"""Generates and returns an x509 certificate for a single user.
Is usually called from a client that will wrap this with
access and secret key info, and return a zip file.
@@ -122,6 +139,8 @@ class AdminController(object):
project = name
project = manager.AuthManager().get_project(project)
user = manager.AuthManager().get_user(name)
+ LOG.audit(_("Getting x509 for user: %s on project: %s"), name,
+ project, context=context)
return user_dict(user, base64.b64encode(project.get_credentials(user)))
def describe_project(self, context, name, **kwargs):
@@ -137,6 +156,8 @@ class AdminController(object):
def register_project(self, context, name, manager_user, description=None,
member_users=None, **kwargs):
"""Creates a new project"""
+ LOG.audit(_("Create project %s managed by %s"), name, manager_user,
+ context=context)
return project_dict(
manager.AuthManager().create_project(
name,
@@ -146,6 +167,7 @@ class AdminController(object):
def deregister_project(self, context, name):
"""Permanently deletes a project."""
+ LOG.audit(_("Delete project: %s"), name, context=context)
manager.AuthManager().delete_project(name)
return True
@@ -159,11 +181,15 @@ class AdminController(object):
**kwargs):
"""Add or remove a user from a project."""
if operation == 'add':
+ LOG.audit(_("Adding user %s to project %s"), user, project,
+ context=context)
manager.AuthManager().add_to_project(user, project)
elif operation == 'remove':
+ LOG.audit(_("Removing user %s from project %s"), user, project,
+ context=context)
manager.AuthManager().remove_from_project(user, project)
else:
- raise exception.ApiError('operation must be add or remove')
+ raise exception.ApiError(_('operation must be add or remove'))
return True
# FIXME(vish): these host commands don't work yet, perhaps some of the
diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py
index a90fbeb0c..d0b417db1 100644
--- a/nova/api/ec2/apirequest.py
+++ b/nova/api/ec2/apirequest.py
@@ -20,13 +20,13 @@
APIRequest class
"""
-import logging
import re
# TODO(termie): replace minidom with etree
from xml.dom import minidom
-_log = logging.getLogger("api")
-_log.setLevel(logging.DEBUG)
+from nova import log as logging
+
+LOG = logging.getLogger("nova.api.request")
_c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
@@ -94,7 +94,7 @@ class APIRequest(object):
except AttributeError:
_error = _('Unsupported API request: controller = %s,'
'action = %s') % (self.controller, self.action)
- _log.warning(_error)
+ LOG.exception(_error)
# TODO: Raise custom exception, trap in apiserver,
# and reraise as 400 error.
raise Exception(_error)
@@ -142,7 +142,7 @@ class APIRequest(object):
response = xml.toxml()
xml.unlink()
- _log.debug(response)
+ LOG.debug(response)
return response
def _render_dict(self, xml, el, data):
@@ -151,7 +151,7 @@ class APIRequest(object):
val = data[key]
el.appendChild(self._render_data(xml, key, val))
except:
- _log.debug(data)
+ LOG.debug(data)
raise
def _render_data(self, xml, el_name, data):
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index b6966e605..b6748e608 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -24,18 +24,16 @@ datastore.
import base64
import datetime
-import logging
-import re
-import os
-
-from nova import context
import IPy
+import os
from nova import compute
+from nova import context
from nova import crypto
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import network
from nova import rpc
from nova import utils
@@ -44,6 +42,9 @@ from nova.compute import instance_types
FLAGS = flags.FLAGS
+flags.DECLARE('service_down_time', 'nova.scheduler.driver')
+
+LOG = logging.getLogger("nova.api.cloud")
InvalidInputException = exception.InvalidInputException
@@ -231,7 +232,7 @@ class CloudController(object):
'zoneState': 'available'}]}
services = db.service_get_all(context)
- now = db.get_time()
+ now = datetime.datetime.utcnow()
hosts = []
for host in [service['host'] for service in services]:
if not host in hosts:
@@ -271,6 +272,7 @@ class CloudController(object):
FLAGS.cc_host,
FLAGS.cc_port,
FLAGS.ec2_suffix)}]
+ return {'regionInfo': regions}
def describe_snapshots(self,
context,
@@ -306,6 +308,7 @@ class CloudController(object):
return {'keypairsSet': result}
def create_key_pair(self, context, key_name, **kwargs):
+ LOG.audit(_("Create key pair %s"), key_name, context=context)
data = _gen_key(context, context.user.id, key_name)
return {'keyName': key_name,
'keyFingerprint': data['fingerprint'],
@@ -313,6 +316,7 @@ class CloudController(object):
# TODO(vish): when context is no longer an object, pass it here
def delete_key_pair(self, context, key_name, **kwargs):
+ LOG.audit(_("Delete key pair %s"), key_name, context=context)
try:
db.key_pair_destroy(context, context.user.id, key_name)
except exception.NotFound:
@@ -419,6 +423,8 @@ class CloudController(object):
return False
def revoke_security_group_ingress(self, context, group_name, **kwargs):
+ LOG.audit(_("Revoke security group ingress %s"), group_name,
+ context=context)
self.compute_api.ensure_default_security_group(context)
security_group = db.security_group_get_by_name(context,
context.project_id,
@@ -436,7 +442,8 @@ class CloudController(object):
match = False
if match:
db.security_group_rule_destroy(context, rule['id'])
- self._trigger_refresh_security_group(context, security_group)
+ self.compute_api.trigger_security_group_rules_refresh(context,
+ security_group['id'])
return True
raise exception.ApiError(_("No rule for the specified parameters."))
@@ -445,6 +452,8 @@ class CloudController(object):
# for these operations, so support for newer API versions
# is sketchy.
def authorize_security_group_ingress(self, context, group_name, **kwargs):
+ LOG.audit(_("Authorize security group ingress %s"), group_name,
+ context=context)
self.compute_api.ensure_default_security_group(context)
security_group = db.security_group_get_by_name(context,
context.project_id,
@@ -459,7 +468,8 @@ class CloudController(object):
security_group_rule = db.security_group_rule_create(context, values)
- self._trigger_refresh_security_group(context, security_group)
+ self.compute_api.trigger_security_group_rules_refresh(context,
+ security_group['id'])
return True
@@ -481,6 +491,7 @@ class CloudController(object):
return source_project_id
def create_security_group(self, context, group_name, group_description):
+ LOG.audit(_("Create Security Group %s"), group_name, context=context)
self.compute_api.ensure_default_security_group(context)
if db.security_group_exists(context, context.project_id, group_name):
raise exception.ApiError(_('group %s already exists') % group_name)
@@ -495,6 +506,7 @@ class CloudController(object):
group_ref)]}
def delete_security_group(self, context, group_name, **kwargs):
+ LOG.audit(_("Delete security group %s"), group_name, context=context)
security_group = db.security_group_get_by_name(context,
context.project_id,
group_name)
@@ -502,6 +514,8 @@ class CloudController(object):
return True
def get_console_output(self, context, instance_id, **kwargs):
+ LOG.audit(_("Get console output for instance %s"), instance_id,
+ context=context)
# instance_id is passed in as a list of instances
ec2_id = instance_id[0]
instance_id = ec2_id_to_id(ec2_id)
@@ -560,6 +574,7 @@ class CloudController(object):
return v
def create_volume(self, context, size, **kwargs):
+ LOG.audit(_("Create volume of %s GB"), size, context=context)
volume = self.volume_api.create(context, size,
kwargs.get('display_name'),
kwargs.get('display_description'))
@@ -583,6 +598,8 @@ class CloudController(object):
return True
def attach_volume(self, context, volume_id, instance_id, device, **kwargs):
+ LOG.audit(_("Attach volume %s to instacne %s at %s"), volume_id,
+ instance_id, device, context=context)
self.compute_api.attach_volume(context, instance_id, volume_id, device)
volume = self.volume_api.get(context, volume_id)
return {'attachTime': volume['attach_time'],
@@ -593,6 +610,7 @@ class CloudController(object):
'volumeId': volume_id}
def detach_volume(self, context, volume_id, **kwargs):
+ LOG.audit(_("Detach volume %s"), volume_id, context=context)
volume = self.volume_api.get(context, volume_id)
instance = self.compute_api.detach_volume(context, volume_id)
return {'attachTime': volume['attach_time'],
@@ -610,19 +628,24 @@ class CloudController(object):
return [{label: x} for x in lst]
def describe_instances(self, context, **kwargs):
- return self._format_describe_instances(context)
+ return self._format_describe_instances(context, **kwargs)
- def _format_describe_instances(self, context):
- return {'reservationSet': self._format_instances(context)}
+ def _format_describe_instances(self, context, **kwargs):
+ return {'reservationSet': self._format_instances(context, **kwargs)}
def _format_run_instances(self, context, reservation_id):
i = self._format_instances(context, reservation_id=reservation_id)
assert len(i) == 1
return i[0]
- def _format_instances(self, context, **kwargs):
+ def _format_instances(self, context, instance_id=None, **kwargs):
reservations = {}
- instances = self.compute_api.get_all(context, **kwargs)
+ # NOTE(vish): instance_id is an optional list of ids to filter by
+ if instance_id:
+ instance_id = [ec2_id_to_id(x) for x in instance_id]
+ instances = [self.compute_api.get(context, x) for x in instance_id]
+ else:
+ instances = self.compute_api.get_all(context, **kwargs)
for instance in instances:
if not context.user.is_admin():
if instance['image_id'] == FLAGS.vpn_image_id:
@@ -696,19 +719,24 @@ class CloudController(object):
return {'addressesSet': addresses}
def allocate_address(self, context, **kwargs):
+ LOG.audit(_("Allocate address"), context=context)
public_ip = self.network_api.allocate_floating_ip(context)
return {'addressSet': [{'publicIp': public_ip}]}
def release_address(self, context, public_ip, **kwargs):
+ LOG.audit(_("Release address %s"), public_ip, context=context)
self.network_api.release_floating_ip(context, public_ip)
return {'releaseResponse': ["Address released."]}
def associate_address(self, context, instance_id, public_ip, **kwargs):
+ LOG.audit(_("Associate address %s to instance %s"), public_ip,
+ instance_id, context=context)
instance_id = ec2_id_to_id(instance_id)
self.compute_api.associate_floating_ip(context, instance_id, public_ip)
return {'associateResponse': ["Address associated."]}
def disassociate_address(self, context, public_ip, **kwargs):
+ LOG.audit(_("Disassociate address %s"), public_ip, context=context)
self.network_api.disassociate_floating_ip(context, public_ip)
return {'disassociateResponse': ["Address disassociated."]}
@@ -735,7 +763,7 @@ class CloudController(object):
def terminate_instances(self, context, instance_id, **kwargs):
"""Terminate each instance in instance_id, which is a list of ec2 ids.
instance_id is a kwarg so its name cannot be modified."""
- logging.debug("Going to start terminating instances")
+ LOG.debug(_("Going to start terminating instances"))
for ec2_id in instance_id:
instance_id = ec2_id_to_id(ec2_id)
self.compute_api.delete(context, instance_id)
@@ -743,6 +771,7 @@ class CloudController(object):
def reboot_instances(self, context, instance_id, **kwargs):
"""instance_id is a list of instance ids"""
+ LOG.audit(_("Reboot instance %r"), instance_id, context=context)
for ec2_id in instance_id:
instance_id = ec2_id_to_id(ec2_id)
self.compute_api.reboot(context, instance_id)
@@ -779,6 +808,7 @@ class CloudController(object):
return {'imagesSet': images}
def deregister_image(self, context, image_id, **kwargs):
+ LOG.audit(_("De-registering image %s"), image_id, context=context)
self.image_service.deregister(context, image_id)
return {'imageId': image_id}
@@ -786,7 +816,8 @@ class CloudController(object):
if image_location is None and 'name' in kwargs:
image_location = kwargs['name']
image_id = self.image_service.register(context, image_location)
- logging.debug("Registered %s as %s" % (image_location, image_id))
+ LOG.audit(_("Registered image %s with id %s"), image_location,
+ image_id, context=context)
return {'imageId': image_id}
def describe_image_attribute(self, context, image_id, attribute, **kwargs):
@@ -814,6 +845,7 @@ class CloudController(object):
raise exception.ApiError(_('only group "all" is supported'))
if not operation_type in ['add', 'remove']:
raise exception.ApiError(_('operation_type must be add or remove'))
+ LOG.audit(_("Updating image %s publicity"), image_id, context=context)
return self.image_service.modify(context, image_id, operation_type)
def update_image(self, context, image_id, **kwargs):
diff --git a/nova/api/ec2/metadatarequesthandler.py b/nova/api/ec2/metadatarequesthandler.py
index a57a6698a..848f0b034 100644
--- a/nova/api/ec2/metadatarequesthandler.py
+++ b/nova/api/ec2/metadatarequesthandler.py
@@ -18,15 +18,15 @@
"""Metadata request handler."""
-import logging
-
import webob.dec
import webob.exc
+from nova import log as logging
from nova import flags
from nova.api.ec2 import cloud
+LOG = logging.getLogger('nova.api.ec2.metadata')
FLAGS = flags.FLAGS
@@ -72,8 +72,7 @@ class MetadataRequestHandler(object):
remote_address = req.headers.get('X-Forwarded-For', remote_address)
meta_data = cc.get_metadata(remote_address)
if meta_data is None:
- logging.error(_('Failed to get metadata for ip: %s') %
- remote_address)
+ LOG.error(_('Failed to get metadata for ip: %s'), remote_address)
raise webob.exc.HTTPNotFound()
data = self.lookup(req.path_info, meta_data)
if data is None:
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index a1430caed..ad203c51f 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -20,28 +20,24 @@
WSGI middleware for OpenStack API controllers.
"""
-import time
-
-import logging
import routes
-import traceback
import webob.dec
import webob.exc
import webob
-from nova import context
from nova import flags
+from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
from nova.api.openstack import backup_schedules
from nova.api.openstack import flavors
from nova.api.openstack import images
-from nova.api.openstack import ratelimiting
from nova.api.openstack import servers
from nova.api.openstack import sharedipgroups
+LOG = logging.getLogger('nova.api.openstack')
FLAGS = flags.FLAGS
flags.DEFINE_string('os_api_auth',
'nova.api.openstack.auth.AuthMiddleware',
@@ -71,8 +67,7 @@ class API(wsgi.Middleware):
try:
return req.get_response(self.application)
except Exception as ex:
- logging.warn(_("Caught error: %s") % str(ex))
- logging.error(traceback.format_exc())
+ LOG.exception(_("Caught error: %s"), str(ex))
exc = webob.exc.HTTPInternalServerError(explanation=str(ex))
return faults.Fault(exc)
@@ -88,7 +83,7 @@ class APIRouter(wsgi.Router):
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
- logging.debug("Including admin operations in API.")
+ LOG.debug(_("Including admin operations in API."))
server_members['pause'] = 'POST'
server_members['unpause'] = 'POST'
server_members["diagnostics"] = "GET"
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ce64ac7ad..3f0fdc575 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -15,13 +15,13 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
import traceback
from webob import exc
from nova import compute
from nova import exception
+from nova import log as logging
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
@@ -170,6 +170,50 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
+ def lock(self, req, id):
+ """
+ lock the instance with id
+ admin only operation
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.lock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::lock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
+ def unlock(self, req, id):
+ """
+ unlock the instance with id
+ admin only operation
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.unlock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::unlock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
+ def get_lock(self, req, id):
+ """
+ return the boolean state of (instance with id)'s lock
+
+ """
+ context = req.environ['nova.context']
+ try:
+ self.compute_api.get_lock(context, id)
+ except:
+ readable = traceback.format_exc()
+ LOG.exception(_("Compute.api::get_lock %s"), readable)
+ return faults.Fault(exc.HTTPUnprocessableEntity())
+ return exc.HTTPAccepted()
+
def pause(self, req, id):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
@@ -177,7 +221,7 @@ class Controller(wsgi.Controller):
self.compute_api.pause(ctxt, id)
except:
readable = traceback.format_exc()
- logging.error(_("Compute.api::pause %s"), readable)
+ LOG.exception(_("Compute.api::pause %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -188,7 +232,7 @@ class Controller(wsgi.Controller):
self.compute_api.unpause(ctxt, id)
except:
readable = traceback.format_exc()
- logging.error(_("Compute.api::unpause %s"), readable)
+ LOG.exception(_("Compute.api::unpause %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -199,7 +243,7 @@ class Controller(wsgi.Controller):
self.compute_api.suspend(context, id)
except:
readable = traceback.format_exc()
- logging.error(_("compute.api::suspend %s"), readable)
+ LOG.exception(_("compute.api::suspend %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -210,7 +254,7 @@ class Controller(wsgi.Controller):
self.compute_api.resume(context, id)
except:
readable = traceback.format_exc()
- logging.error(_("compute.api::resume %s"), readable)
+ LOG.exception(_("compute.api::resume %s"), readable)
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
@@ -222,4 +266,13 @@ class Controller(wsgi.Controller):
def actions(self, req, id):
"""Permit Admins to retrieve server actions."""
ctxt = req.environ["nova.context"]
- return self.compute_api.get_actions(ctxt, id)
+ items = self.compute_api.get_actions(ctxt, id)
+ actions = []
+ # TODO(jk0): Do not do pre-serialization here once the default
+ # serializer is updated
+ for item in items:
+ actions.append(dict(
+ created_at=str(item.created_at),
+ action=item.action,
+ error=item.error))
+ return dict(actions=actions)
diff --git a/nova/auth/dbdriver.py b/nova/auth/dbdriver.py
index 47e435cb6..0eb6fe588 100644
--- a/nova/auth/dbdriver.py
+++ b/nova/auth/dbdriver.py
@@ -20,7 +20,6 @@
Auth driver using the DB as its backend.
"""
-import logging
import sys
from nova import context
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index 7616ff112..c8de20028 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -24,11 +24,11 @@ other backends by creating another class that exposes the same
public methods.
"""
-import logging
import sys
from nova import exception
from nova import flags
+from nova import log as logging
FLAGS = flags.FLAGS
@@ -65,6 +65,8 @@ flags.DEFINE_string('ldap_netadmin',
flags.DEFINE_string('ldap_developer',
'cn=developers,ou=Groups,dc=example,dc=com', 'cn for Developers')
+LOG = logging.getLogger("nova.ldapdriver")
+
# TODO(vish): make an abstract base class with the same public methods
# to define a set interface for AuthDrivers. I'm delaying
@@ -502,8 +504,8 @@ class LdapDriver(object):
try:
self.conn.modify_s(group_dn, attr)
except self.ldap.OBJECT_CLASS_VIOLATION:
- logging.debug(_("Attempted to remove the last member of a group. "
- "Deleting the group at %s instead."), group_dn)
+ LOG.debug(_("Attempted to remove the last member of a group. "
+ "Deleting the group at %s instead."), group_dn)
self.__delete_group(group_dn)
def __remove_from_all(self, uid):
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index d3e266952..5685ae5e2 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -20,7 +20,6 @@
Nova authentication management
"""
-import logging
import os
import shutil
import string # pylint: disable-msg=W0402
@@ -33,6 +32,7 @@ from nova import crypto
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
from nova.auth import signer
@@ -70,6 +70,8 @@ flags.DEFINE_string('credential_rc_file', '%src',
flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
'Driver that auth manager uses')
+LOG = logging.getLogger('nova.auth.manager')
+
class AuthBase(object):
"""Base class for objects relating to auth
@@ -254,43 +256,51 @@ class AuthManager(object):
# TODO(vish): check for valid timestamp
(access_key, _sep, project_id) = access.partition(':')
- logging.info(_('Looking up user: %r'), access_key)
+ LOG.debug(_('Looking up user: %r'), access_key)
user = self.get_user_from_access_key(access_key)
- logging.info('user: %r', user)
+ LOG.debug('user: %r', user)
if user == None:
+ LOG.audit(_("Failed authorization for access key %s"), access_key)
raise exception.NotFound(_('No user found for access key %s')
% access_key)
# NOTE(vish): if we stop using project name as id we need better
# logic to find a default project for user
if project_id == '':
+ LOG.debug(_("Using project name = user name (%s)"), user.name)
project_id = user.name
project = self.get_project(project_id)
if project == None:
+ LOG.audit(_("failed authorization: no project named %s (user=%s)"),
+ project_id, user.name)
raise exception.NotFound(_('No project called %s could be found')
% project_id)
if not self.is_admin(user) and not self.is_project_member(user,
project):
+ LOG.audit(_("Failed authorization: user %s not admin and not "
+ "member of project %s"), user.name, project.name)
raise exception.NotFound(_('User %s is not a member of project %s')
% (user.id, project.id))
if check_type == 's3':
sign = signer.Signer(user.secret.encode())
expected_signature = sign.s3_authorization(headers, verb, path)
- logging.debug('user.secret: %s', user.secret)
- logging.debug('expected_signature: %s', expected_signature)
- logging.debug('signature: %s', signature)
+ LOG.debug('user.secret: %s', user.secret)
+ LOG.debug('expected_signature: %s', expected_signature)
+ LOG.debug('signature: %s', signature)
if signature != expected_signature:
+ LOG.audit(_("Invalid signature for user %s"), user.name)
raise exception.NotAuthorized(_('Signature does not match'))
elif check_type == 'ec2':
# NOTE(vish): hmac can't handle unicode, so encode ensures that
# secret isn't unicode
expected_signature = signer.Signer(user.secret.encode()).generate(
params, verb, server_string, path)
- logging.debug('user.secret: %s', user.secret)
- logging.debug('expected_signature: %s', expected_signature)
- logging.debug('signature: %s', signature)
+ LOG.debug('user.secret: %s', user.secret)
+ LOG.debug('expected_signature: %s', expected_signature)
+ LOG.debug('signature: %s', signature)
if signature != expected_signature:
+ LOG.audit(_("Invalid signature for user %s"), user.name)
raise exception.NotAuthorized(_('Signature does not match'))
return (user, project)
@@ -398,6 +408,12 @@ class AuthManager(object):
raise exception.NotFound(_("The %s role can not be found") % role)
if project is not None and role in FLAGS.global_roles:
raise exception.NotFound(_("The %s role is global only") % role)
+ if project:
+ LOG.audit(_("Adding role %s to user %s in project %s"), role,
+ User.safe_id(user), Project.safe_id(project))
+ else:
+ LOG.audit(_("Adding sitewide role %s to user %s"), role,
+ User.safe_id(user))
with self.driver() as drv:
drv.add_role(User.safe_id(user), role, Project.safe_id(project))
@@ -418,6 +434,12 @@ class AuthManager(object):
@type project: Project or project_id
@param project: Project in which to remove local role.
"""
+ if project:
+ LOG.audit(_("Removing role %s from user %s on project %s"),
+ role, User.safe_id(user), Project.safe_id(project))
+ else:
+ LOG.audit(_("Removing sitewide role %s from user %s"), role,
+ User.safe_id(user))
with self.driver() as drv:
drv.remove_role(User.safe_id(user), role, Project.safe_id(project))
@@ -480,6 +502,8 @@ class AuthManager(object):
description,
member_users)
if project_dict:
+ LOG.audit(_("Created project %s with manager %s"), name,
+ manager_user)
project = Project(**project_dict)
return project
@@ -496,6 +520,7 @@ class AuthManager(object):
@param project: This will be the new description of the project.
"""
+ LOG.audit(_("modifying project %s"), Project.safe_id(project))
if manager_user:
manager_user = User.safe_id(manager_user)
with self.driver() as drv:
@@ -505,6 +530,8 @@ class AuthManager(object):
def add_to_project(self, user, project):
"""Add user to project"""
+ LOG.audit(_("Adding user %s to project %s"), User.safe_id(user),
+ Project.safe_id(project))
with self.driver() as drv:
return drv.add_to_project(User.safe_id(user),
Project.safe_id(project))
@@ -523,6 +550,8 @@ class AuthManager(object):
def remove_from_project(self, user, project):
"""Removes a user from a project"""
+ LOG.audit(_("Remove user %s from project %s"), User.safe_id(user),
+ Project.safe_id(project))
with self.driver() as drv:
return drv.remove_from_project(User.safe_id(user),
Project.safe_id(project))
@@ -549,6 +578,7 @@ class AuthManager(object):
def delete_project(self, project):
"""Deletes a project"""
+ LOG.audit(_("Deleting project %s"), Project.safe_id(project))
with self.driver() as drv:
drv.delete_project(Project.safe_id(project))
@@ -603,13 +633,16 @@ class AuthManager(object):
with self.driver() as drv:
user_dict = drv.create_user(name, access, secret, admin)
if user_dict:
- return User(**user_dict)
+ rv = User(**user_dict)
+ LOG.audit(_("Created user %s (admin: %r)"), rv.name, rv.admin)
+ return rv
def delete_user(self, user):
"""Deletes a user
Additionally deletes all users key_pairs"""
uid = User.safe_id(user)
+ LOG.audit(_("Deleting user %s"), uid)
db.key_pair_destroy_all_by_user(context.get_admin_context(),
uid)
with self.driver() as drv:
@@ -618,6 +651,12 @@ class AuthManager(object):
def modify_user(self, user, access_key=None, secret_key=None, admin=None):
"""Modify credentials for a user"""
uid = User.safe_id(user)
+ if access_key:
+ LOG.audit(_("Access Key change for user %s"), uid)
+ if secret_key:
+ LOG.audit(_("Secret Key change for user %s"), uid)
+ if admin is not None:
+ LOG.audit(_("Admin status set to %r for user %s"), admin, uid)
with self.driver() as drv:
drv.modify_user(uid, access_key, secret_key, admin)
@@ -666,7 +705,7 @@ class AuthManager(object):
port=vpn_port)
zippy.writestr(FLAGS.credential_vpn_file, config)
else:
- logging.warn(_("No vpn data for project %s"), pid)
+ LOG.warn(_("No vpn data for project %s"), pid)
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
zippy.close()
diff --git a/nova/auth/signer.py b/nova/auth/signer.py
index f7d29f534..744e315d4 100644
--- a/nova/auth/signer.py
+++ b/nova/auth/signer.py
@@ -46,7 +46,6 @@ Utility class for parsing signed AMI manifests.
import base64
import hashlib
import hmac
-import logging
import urllib
# NOTE(vish): for new boto
@@ -54,9 +53,13 @@ import boto
# NOTE(vish): for old boto
import boto.utils
+from nova import log as logging
from nova.exception import Error
+LOG = logging.getLogger('nova.signer')
+
+
class Signer(object):
"""Hacked up code from boto/connection.py"""
@@ -120,7 +123,7 @@ class Signer(object):
def _calc_signature_2(self, params, verb, server_string, path):
"""Generate AWS signature version 2 string."""
- logging.debug('using _calc_signature_2')
+ LOG.debug('using _calc_signature_2')
string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path)
if self.hmac_256:
current_hmac = self.hmac_256
@@ -136,13 +139,13 @@ class Signer(object):
val = urllib.quote(val, safe='-_~')
pairs.append(urllib.quote(key, safe='') + '=' + val)
qs = '&'.join(pairs)
- logging.debug('query string: %s', qs)
+ LOG.debug('query string: %s', qs)
string_to_sign += qs
- logging.debug('string_to_sign: %s', string_to_sign)
+ LOG.debug('string_to_sign: %s', string_to_sign)
current_hmac.update(string_to_sign)
b64 = base64.b64encode(current_hmac.digest())
- logging.debug('len(b64)=%d', len(b64))
- logging.debug('base64 encoded digest: %s', b64)
+ LOG.debug('len(b64)=%d', len(b64))
+ LOG.debug('base64 encoded digest: %s', b64)
return b64
diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py
index 09361828d..8aefd341f 100644
--- a/nova/cloudpipe/pipelib.py
+++ b/nova/cloudpipe/pipelib.py
@@ -22,7 +22,6 @@ an instance with it.
"""
-import logging
import os
import string
import tempfile
@@ -33,6 +32,7 @@ from nova import crypto
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
from nova.auth import manager
# TODO(eday): Eventually changes these to something not ec2-specific
@@ -51,7 +51,7 @@ flags.DEFINE_string('dmz_mask',
_('Netmask to push into openvpn config'))
-LOG = logging.getLogger('nova-cloudpipe')
+LOG = logging.getLogger('nova.cloudpipe')
class CloudPipe(object):
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 3ba91fe05..ff2f756f1 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -21,12 +21,12 @@ Handles all requests relating to instances (guest vms).
"""
import datetime
-import logging
import time
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import network
from nova import quota
from nova import rpc
@@ -36,6 +36,7 @@ from nova.compute import instance_types
from nova.db import base
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.compute.api')
def generate_default_hostname(instance_id):
@@ -63,13 +64,13 @@ class API(base.Base):
try:
instance = self.get(context, instance_id)
except exception.NotFound as e:
- logging.warning("Instance %d was not found in get_network_topic",
- instance_id)
+ LOG.warning(_("Instance %d was not found in get_network_topic"),
+ instance_id)
raise e
host = instance['host']
if not host:
- raise exception.Error("Instance %d has no host" % instance_id)
+ raise exception.Error(_("Instance %d has no host") % instance_id)
topic = self.db.queue_get_for(context, FLAGS.compute_topic, host)
return rpc.call(context,
topic,
@@ -88,10 +89,10 @@ class API(base.Base):
type_data = instance_types.INSTANCE_TYPES[instance_type]
num_instances = quota.allowed_instances(context, max_count, type_data)
if num_instances < min_count:
- logging.warn("Quota exceeeded for %s, tried to run %s instances",
- context.project_id, min_count)
- raise quota.QuotaError("Instance quota exceeded. You can only "
- "run %s more instances of this type." %
+ LOG.warn(_("Quota exceeeded for %s, tried to run %s instances"),
+ context.project_id, min_count)
+ raise quota.QuotaError(_("Instance quota exceeded. You can only "
+ "run %s more instances of this type.") %
num_instances, "InstanceLimitExceeded")
is_vpn = image_id == FLAGS.vpn_image_id
@@ -105,7 +106,7 @@ class API(base.Base):
if kernel_id == str(FLAGS.null_kernel):
kernel_id = None
ramdisk_id = None
- logging.debug("Creating a raw instance")
+ LOG.debug(_("Creating a raw instance"))
# Make sure we have access to kernel and ramdisk (if not raw)
if kernel_id:
self.image_service.show(context, kernel_id)
@@ -147,11 +148,12 @@ class API(base.Base):
'user_data': user_data or '',
'key_name': key_name,
'key_data': key_data,
+ 'locked': False,
'availability_zone': availability_zone}
elevated = context.elevated()
instances = []
- logging.debug(_("Going to run %s instances..."), num_instances)
+ LOG.debug(_("Going to run %s instances..."), num_instances)
for num in range(num_instances):
instance = dict(mac_address=utils.generate_mac(),
launch_index=num,
@@ -175,7 +177,7 @@ class API(base.Base):
instance = self.update(context, instance_id, **updates)
instances.append(instance)
- logging.debug(_("Casting to scheduler for %s/%s's instance %s"),
+ LOG.debug(_("Casting to scheduler for %s/%s's instance %s"),
context.project_id, context.user_id, instance_id)
rpc.cast(context,
FLAGS.scheduler_topic,
@@ -184,6 +186,9 @@ class API(base.Base):
"instance_id": instance_id,
"availability_zone": availability_zone}})
+ for group_id in security_groups:
+ self.trigger_security_group_members_refresh(elevated, group_id)
+
return instances
def ensure_default_security_group(self, context):
@@ -203,6 +208,60 @@ class API(base.Base):
'project_id': context.project_id}
db.security_group_create(context, values)
+ def trigger_security_group_rules_refresh(self, context, security_group_id):
+ """Called when a rule is added to or removed from a security_group"""
+
+ security_group = self.db.security_group_get(context, security_group_id)
+
+ hosts = set()
+ for instance in security_group['instances']:
+ if instance['host'] is not None:
+ hosts.add(instance['host'])
+
+ for host in hosts:
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "refresh_security_group_rules",
+ "args": {"security_group_id": security_group.id}})
+
+ def trigger_security_group_members_refresh(self, context, group_id):
+ """Called when a security group gains a new or loses a member
+
+ Sends an update request to each compute node for whom this is
+ relevant."""
+
+ # First, we get the security group rules that reference this group as
+ # the grantee..
+ security_group_rules = \
+ self.db.security_group_rule_get_by_security_group_grantee(
+ context,
+ group_id)
+
+ # ..then we distill the security groups to which they belong..
+ security_groups = set()
+ for rule in security_group_rules:
+ security_groups.add(rule['parent_group_id'])
+
+ # ..then we find the instances that are members of these groups..
+ instances = set()
+ for security_group in security_groups:
+ for instance in security_group['instances']:
+ instances.add(instance['id'])
+
+ # ...then we find the hosts where they live...
+ hosts = set()
+ for instance in instances:
+ if instance['host']:
+ hosts.add(instance['host'])
+
+ # ...and finally we tell these nodes to refresh their view of this
+ # particular security group.
+ for host in hosts:
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "refresh_security_group_members",
+ "args": {"security_group_id": group_id}})
+
def update(self, context, instance_id, **kwargs):
"""Updates the instance in the datastore.
@@ -218,17 +277,17 @@ class API(base.Base):
return self.db.instance_update(context, instance_id, kwargs)
def delete(self, context, instance_id):
- logging.debug("Going to try and terminate %s" % instance_id)
+ LOG.debug(_("Going to try and terminate %s"), instance_id)
try:
instance = self.get(context, instance_id)
except exception.NotFound as e:
- logging.warning(_("Instance %s was not found during terminate"),
- instance_id)
+ LOG.warning(_("Instance %d was not found during terminate"),
+ instance_id)
raise e
if (instance['state_description'] == 'terminating'):
- logging.warning(_("Instance %s is already being terminated"),
- instance_id)
+ LOG.warning(_("Instance %d is already being terminated"),
+ instance_id)
return
self.update(context,
@@ -355,6 +414,38 @@ class API(base.Base):
{"method": "unrescue_instance",
"args": {"instance_id": instance_id}})
+ def lock(self, context, instance_id):
+ """
+ lock the instance with instance_id
+
+ """
+ instance = self.get_instance(context, instance_id)
+ host = instance['host']
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "lock_instance",
+ "args": {"instance_id": instance['id']}})
+
+ def unlock(self, context, instance_id):
+ """
+ unlock the instance with instance_id
+
+ """
+ instance = self.get_instance(context, instance_id)
+ host = instance['host']
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "unlock_instance",
+ "args": {"instance_id": instance['id']}})
+
+ def get_lock(self, context, instance_id):
+ """
+ return the boolean state of (instance with instance_id)'s lock
+
+ """
+ instance = self.get_instance(context, instance_id)
+ return instance['locked']
+
def attach_volume(self, context, instance_id, volume_id, device):
if not re.match("^/dev/[a-z]d[a-z]+$", device):
raise exception.ApiError(_("Invalid device specified: %s. "
diff --git a/nova/compute/disk.py b/nova/compute/disk.py
index 814a258cd..741499294 100644
--- a/nova/compute/disk.py
+++ b/nova/compute/disk.py
@@ -22,14 +22,15 @@ Includes injection of SSH PGP keys into authorized_keys file.
"""
-import logging
import os
import tempfile
from nova import exception
from nova import flags
+from nova import log as logging
+LOG = logging.getLogger('nova.compute.disk')
FLAGS = flags.FLAGS
flags.DEFINE_integer('minimum_root_size', 1024 * 1024 * 1024 * 10,
'minimum size in bytes of root partition')
@@ -67,12 +68,12 @@ def partition(infile, outfile, local_bytes=0, resize=True,
execute('resize2fs %s' % infile)
file_size = FLAGS.minimum_root_size
elif file_size % sector_size != 0:
- logging.warn(_("Input partition size not evenly divisible by"
- " sector size: %d / %d"), file_size, sector_size)
+ LOG.warn(_("Input partition size not evenly divisible by"
+ " sector size: %d / %d"), file_size, sector_size)
primary_sectors = file_size / sector_size
if local_bytes % sector_size != 0:
- logging.warn(_("Bytes for local storage not evenly divisible"
- " by sector size: %d / %d"), local_bytes, sector_size)
+ LOG.warn(_("Bytes for local storage not evenly divisible"
+ " by sector size: %d / %d"), local_bytes, sector_size)
local_sectors = local_bytes / sector_size
mbr_last = 62 # a
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index ca6065890..6ae9b689a 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -35,10 +35,11 @@ terminating it.
"""
import datetime
-import logging
+import functools
from nova import exception
from nova import flags
+from nova import log as logging
from nova import manager
from nova import rpc
from nova import utils
@@ -52,6 +53,42 @@ flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection',
flags.DEFINE_string('stub_network', False,
'Stub network related code')
+LOG = logging.getLogger('nova.compute.manager')
+
+
+def checks_instance_lock(function):
+ """
+ decorator used for preventing action against locked instances
+ unless, of course, you happen to be admin
+
+ """
+
+ @functools.wraps(function)
+ def decorated_function(self, context, instance_id, *args, **kwargs):
+
+ LOG.info(_("check_instance_lock: decorating: |%s|"), function,
+ context=context)
+ LOG.info(_("check_instance_lock: arguments: |%s| |%s| |%s|"),
+ self, context, instance_id, context=context)
+ locked = self.get_lock(context, instance_id)
+ admin = context.is_admin
+ LOG.info(_("check_instance_lock: locked: |%s|"), locked,
+ context=context)
+ LOG.info(_("check_instance_lock: admin: |%s|"), admin,
+ context=context)
+
+ # if admin or unlocked call function otherwise log error
+ if admin or not locked:
+ LOG.info(_("check_instance_lock: executing: |%s|"), function,
+ context=context)
+ function(self, context, instance_id, *args, **kwargs)
+ else:
+ LOG.error(_("check_instance_lock: not executing |%s|"),
+ function, context=context)
+ return False
+
+ return decorated_function
+
class ComputeManager(manager.Manager):
@@ -100,9 +137,16 @@ class ComputeManager(manager.Manager):
host)
@exception.wrap_exception
- def refresh_security_group(self, context, security_group_id, **_kwargs):
- """This call passes stright through to the virtualization driver."""
- self.driver.refresh_security_group(security_group_id)
+ def refresh_security_group_rules(self, context,
+ security_group_id, **_kwargs):
+ """This call passes straight through to the virtualization driver."""
+ return self.driver.refresh_security_group_rules(security_group_id)
+
+ @exception.wrap_exception
+ def refresh_security_group_members(self, context,
+ security_group_id, **_kwargs):
+ """This call passes straight through to the virtualization driver."""
+ return self.driver.refresh_security_group_members(security_group_id)
@exception.wrap_exception
def run_instance(self, context, instance_id, **_kwargs):
@@ -111,7 +155,8 @@ class ComputeManager(manager.Manager):
instance_ref = self.db.instance_get(context, instance_id)
if instance_ref['name'] in self.driver.list_instances():
raise exception.Error(_("Instance has already been created"))
- logging.debug(_("instance %s: starting..."), instance_id)
+ LOG.audit(_("instance %s: starting..."), instance_id,
+ context=context)
self.db.instance_update(context,
instance_id,
{'host': self.host})
@@ -149,8 +194,8 @@ class ComputeManager(manager.Manager):
instance_id,
{'launched_at': now})
except Exception: # pylint: disable-msg=W0702
- logging.exception(_("instance %s: Failed to spawn"),
- instance_ref['name'])
+ LOG.exception(_("instance %s: Failed to spawn"), instance_id,
+ context=context)
self.db.instance_set_state(context,
instance_id,
power_state.SHUTDOWN)
@@ -158,17 +203,19 @@ class ComputeManager(manager.Manager):
self._update_state(context, instance_id)
@exception.wrap_exception
+ @checks_instance_lock
def terminate_instance(self, context, instance_id):
"""Terminate an instance on this machine."""
context = context.elevated()
-
instance_ref = self.db.instance_get(context, instance_id)
+ LOG.audit(_("Terminating instance %s"), instance_id, context=context)
if not FLAGS.stub_network:
address = self.db.instance_get_floating_address(context,
instance_ref['id'])
if address:
- logging.debug(_("Disassociating address %s") % address)
+ LOG.debug(_("Disassociating address %s"), address,
+ context=context)
# NOTE(vish): Right now we don't really care if the ip is
# disassociated. We may need to worry about
# checking this later.
@@ -180,15 +227,14 @@ class ComputeManager(manager.Manager):
address = self.db.instance_get_fixed_address(context,
instance_ref['id'])
if address:
- logging.debug(_("Deallocating address %s") % address)
+ LOG.debug(_("Deallocating address %s"), address,
+ context=context)
# NOTE(vish): Currently, nothing needs to be done on the
# network node until release. If this changes,
# we will need to cast here.
self.network_manager.deallocate_fixed_ip(context.elevated(),
address)
- logging.debug(_("instance %s: terminating"), instance_id)
-
volumes = instance_ref.get('volumes', []) or []
for volume in volumes:
self.detach_volume(context, instance_id, volume['id'])
@@ -202,20 +248,22 @@ class ComputeManager(manager.Manager):
self.db.instance_destroy(context, instance_id)
@exception.wrap_exception
+ @checks_instance_lock
def reboot_instance(self, context, instance_id):
"""Reboot an instance on this server."""
context = context.elevated()
self._update_state(context, instance_id)
instance_ref = self.db.instance_get(context, instance_id)
+ LOG.audit(_("Rebooting instance %s"), instance_id, context=context)
if instance_ref['state'] != power_state.RUNNING:
- logging.warn(_('trying to reboot a non-running '
- 'instance: %s (state: %s excepted: %s)'),
- instance_id,
- instance_ref['state'],
- power_state.RUNNING)
+ LOG.warn(_('trying to reboot a non-running '
+ 'instance: %s (state: %s excepted: %s)'),
+ instance_id,
+ instance_ref['state'],
+ power_state.RUNNING,
+ context=context)
- logging.debug(_('instance %s: rebooting'), instance_ref['name'])
self.db.instance_set_state(context,
instance_id,
power_state.NOSTATE,
@@ -235,23 +283,22 @@ class ComputeManager(manager.Manager):
# potentially?
self._update_state(context, instance_id)
- logging.debug(_('instance %s: snapshotting'), instance_ref['name'])
+ LOG.audit(_('instance %s: snapshotting'), instance_id,
+ context=context)
if instance_ref['state'] != power_state.RUNNING:
- logging.warn(_('trying to snapshot a non-running '
- 'instance: %s (state: %s excepted: %s)'),
- instance_id,
- instance_ref['state'],
- power_state.RUNNING)
+ LOG.warn(_('trying to snapshot a non-running '
+ 'instance: %s (state: %s excepted: %s)'),
+ instance_id, instance_ref['state'], power_state.RUNNING)
self.driver.snapshot(instance_ref, name)
@exception.wrap_exception
+ @checks_instance_lock
def rescue_instance(self, context, instance_id):
"""Rescue an instance on this server."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug(_('instance %s: rescuing'), instance_id)
+ LOG.audit(_('instance %s: rescuing'), instance_id, context=context)
self.db.instance_set_state(context,
instance_id,
power_state.NOSTATE,
@@ -261,12 +308,12 @@ class ComputeManager(manager.Manager):
self._update_state(context, instance_id)
@exception.wrap_exception
+ @checks_instance_lock
def unrescue_instance(self, context, instance_id):
"""Rescue an instance on this server."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug(_('instance %s: unrescuing'), instance_id)
+ LOG.audit(_('instance %s: unrescuing'), instance_id, context=context)
self.db.instance_set_state(context,
instance_id,
power_state.NOSTATE,
@@ -280,12 +327,12 @@ class ComputeManager(manager.Manager):
self._update_state(context, instance_id)
@exception.wrap_exception
+ @checks_instance_lock
def pause_instance(self, context, instance_id):
"""Pause an instance on this server."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug('instance %s: pausing', instance_id)
+ LOG.audit(_('instance %s: pausing'), instance_id, context=context)
self.db.instance_set_state(context,
instance_id,
power_state.NOSTATE,
@@ -297,12 +344,12 @@ class ComputeManager(manager.Manager):
result))
@exception.wrap_exception
+ @checks_instance_lock
def unpause_instance(self, context, instance_id):
"""Unpause a paused instance on this server."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug('instance %s: unpausing', instance_id)
+ LOG.audit(_('instance %s: unpausing'), instance_id, context=context)
self.db.instance_set_state(context,
instance_id,
power_state.NOSTATE,
@@ -319,17 +366,20 @@ class ComputeManager(manager.Manager):
instance_ref = self.db.instance_get(context, instance_id)
if instance_ref["state"] == power_state.RUNNING:
- logging.debug(_("instance %s: retrieving diagnostics"),
- instance_id)
+ LOG.audit(_("instance %s: retrieving diagnostics"), instance_id,
+ context=context)
return self.driver.get_diagnostics(instance_ref)
@exception.wrap_exception
+ @checks_instance_lock
def suspend_instance(self, context, instance_id):
- """suspend the instance with instance_id"""
+ """
+ suspend the instance with instance_id
+
+ """
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug(_('instance %s: suspending'), instance_id)
+ LOG.audit(_('instance %s: suspending'), instance_id, context=context)
self.db.instance_set_state(context, instance_id,
power_state.NOSTATE,
'suspending')
@@ -340,12 +390,15 @@ class ComputeManager(manager.Manager):
result))
@exception.wrap_exception
+ @checks_instance_lock
def resume_instance(self, context, instance_id):
- """resume the suspended instance with instance_id"""
+ """
+ resume the suspended instance with instance_id
+
+ """
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
-
- logging.debug(_('instance %s: resuming'), instance_id)
+ LOG.audit(_('instance %s: resuming'), instance_id, context=context)
self.db.instance_set_state(context, instance_id,
power_state.NOSTATE,
'resuming')
@@ -356,21 +409,58 @@ class ComputeManager(manager.Manager):
result))
@exception.wrap_exception
+ def lock_instance(self, context, instance_id):
+ """
+ lock the instance with instance_id
+
+ """
+ context = context.elevated()
+ instance_ref = self.db.instance_get(context, instance_id)
+
+ LOG.debug(_('instance %s: locking'), instance_id, context=context)
+ self.db.instance_update(context, instance_id, {'locked': True})
+
+ @exception.wrap_exception
+ def unlock_instance(self, context, instance_id):
+ """
+ unlock the instance with instance_id
+
+ """
+ context = context.elevated()
+ instance_ref = self.db.instance_get(context, instance_id)
+
+ LOG.debug(_('instance %s: unlocking'), instance_id, context=context)
+ self.db.instance_update(context, instance_id, {'locked': False})
+
+ @exception.wrap_exception
+ def get_lock(self, context, instance_id):
+ """
+ return the boolean state of (instance with instance_id)'s lock
+
+ """
+ context = context.elevated()
+ LOG.debug(_('instance %s: getting locked state'), instance_id,
+ context=context)
+ instance_ref = self.db.instance_get(context, instance_id)
+ return instance_ref['locked']
+
+ @exception.wrap_exception
def get_console_output(self, context, instance_id):
"""Send the console output for an instance."""
context = context.elevated()
- logging.debug(_("instance %s: getting console output"), instance_id)
instance_ref = self.db.instance_get(context, instance_id)
-
+ LOG.audit(_("Get console output for instance %s"), instance_id,
+ context=context)
return self.driver.get_console_output(instance_ref)
@exception.wrap_exception
+ @checks_instance_lock
def attach_volume(self, context, instance_id, volume_id, mountpoint):
"""Attach a volume to an instance."""
context = context.elevated()
- logging.debug(_("instance %s: attaching volume %s to %s"), instance_id,
- volume_id, mountpoint)
instance_ref = self.db.instance_get(context, instance_id)
+ LOG.audit(_("instance %s: attaching volume %s to %s"), instance_id,
+ volume_id, mountpoint, context=context)
dev_path = self.volume_manager.setup_compute_volume(context,
volume_id)
try:
@@ -385,8 +475,8 @@ class ComputeManager(manager.Manager):
# NOTE(vish): The inline callback eats the exception info so we
# log the traceback here and reraise the same
# ecxception below.
- logging.exception(_("instance %s: attach failed %s, removing"),
- instance_id, mountpoint)
+ LOG.exception(_("instance %s: attach failed %s, removing"),
+ instance_id, mountpoint, context=context)
self.volume_manager.remove_compute_volume(context,
volume_id)
raise exc
@@ -394,17 +484,18 @@ class ComputeManager(manager.Manager):
return True
@exception.wrap_exception
+ @checks_instance_lock
def detach_volume(self, context, instance_id, volume_id):
"""Detach a volume from an instance."""
context = context.elevated()
- logging.debug(_("instance %s: detaching volume %s"),
- instance_id,
- volume_id)
instance_ref = self.db.instance_get(context, instance_id)
volume_ref = self.db.volume_get(context, volume_id)
+ LOG.audit(_("Detach volume %s from mountpoint %s on instance %s"),
+ volume_id, volume_ref['mountpoint'], instance_id,
+ context=context)
if instance_ref['name'] not in self.driver.list_instances():
- logging.warn(_("Detaching volume from unknown instance %s"),
- instance_ref['name'])
+ LOG.warn(_("Detaching volume from unknown instance %s"),
+ instance_id, context=context)
else:
self.driver.detach_volume(instance_ref['name'],
volume_ref['mountpoint'])
diff --git a/nova/compute/monitor.py b/nova/compute/monitor.py
index 60c347a5e..14d0e8ca1 100644
--- a/nova/compute/monitor.py
+++ b/nova/compute/monitor.py
@@ -25,19 +25,17 @@ Instance Monitoring:
"""
import datetime
-import logging
import os
-import sys
import time
import boto
import boto.s3
import rrdtool
-from twisted.internet import defer
from twisted.internet import task
from twisted.application import service
from nova import flags
+from nova import log as logging
from nova.virt import connection as virt_connection
@@ -91,6 +89,9 @@ RRD_VALUES = {
utcnow = datetime.datetime.utcnow
+LOG = logging.getLogger('nova.compute.monitor')
+
+
def update_rrd(instance, name, data):
"""
Updates the specified RRD file.
@@ -255,20 +256,20 @@ class Instance(object):
Updates the instances statistics and stores the resulting graphs
in the internal object store on the cloud controller.
"""
- logging.debug(_('updating %s...'), self.instance_id)
+ LOG.debug(_('updating %s...'), self.instance_id)
try:
data = self.fetch_cpu_stats()
if data != None:
- logging.debug('CPU: %s', data)
+ LOG.debug('CPU: %s', data)
update_rrd(self, 'cpu', data)
data = self.fetch_net_stats()
- logging.debug('NET: %s', data)
+ LOG.debug('NET: %s', data)
update_rrd(self, 'net', data)
data = self.fetch_disk_stats()
- logging.debug('DISK: %s', data)
+ LOG.debug('DISK: %s', data)
update_rrd(self, 'disk', data)
# TODO(devcamcar): Turn these into pool.ProcessPool.execute() calls
@@ -285,7 +286,7 @@ class Instance(object):
graph_disk(self, '1w')
graph_disk(self, '1m')
except Exception:
- logging.exception(_('unexpected error during update'))
+ LOG.exception(_('unexpected error during update'))
self.last_updated = utcnow()
@@ -309,7 +310,7 @@ class Instance(object):
self.cputime = float(info['cpu_time'])
self.cputime_last_updated = utcnow()
- logging.debug('CPU: %d', self.cputime)
+ LOG.debug('CPU: %d', self.cputime)
# Skip calculation on first pass. Need delta to get a meaningful value.
if cputime_last_updated == None:
@@ -319,17 +320,17 @@ class Instance(object):
d = self.cputime_last_updated - cputime_last_updated
t = d.days * 86400 + d.seconds
- logging.debug('t = %d', t)
+ LOG.debug('t = %d', t)
# Calculate change over time in number of nanoseconds of CPU time used.
cputime_delta = self.cputime - cputime_last
- logging.debug('cputime_delta = %s', cputime_delta)
+ LOG.debug('cputime_delta = %s', cputime_delta)
# Get the number of virtual cpus in this domain.
vcpus = int(info['num_cpu'])
- logging.debug('vcpus = %d', vcpus)
+ LOG.debug('vcpus = %d', vcpus)
# Calculate CPU % used and cap at 100.
return min(cputime_delta / (t * vcpus * 1.0e9) * 100, 100)
@@ -351,8 +352,8 @@ class Instance(object):
rd += rd_bytes
wr += wr_bytes
except TypeError:
- logging.error(_('Cannot get blockstats for "%s" on "%s"'),
- disk, self.instance_id)
+ LOG.error(_('Cannot get blockstats for "%s" on "%s"'),
+ disk, self.instance_id)
raise
return '%d:%d' % (rd, wr)
@@ -373,8 +374,8 @@ class Instance(object):
rx += stats[0]
tx += stats[4]
except TypeError:
- logging.error(_('Cannot get ifstats for "%s" on "%s"'),
- interface, self.instance_id)
+ LOG.error(_('Cannot get ifstats for "%s" on "%s"'),
+ interface, self.instance_id)
raise
return '%d:%d' % (rx, tx)
@@ -408,7 +409,7 @@ class InstanceMonitor(object, service.Service):
try:
conn = virt_connection.get_connection(read_only=True)
except Exception, exn:
- logging.exception(_('unexpected exception getting connection'))
+ LOG.exception(_('unexpected exception getting connection'))
time.sleep(FLAGS.monitoring_instances_delay)
return
@@ -416,14 +417,14 @@ class InstanceMonitor(object, service.Service):
try:
self.updateInstances_(conn, domain_ids)
except Exception, exn:
- logging.exception('updateInstances_')
+ LOG.exception('updateInstances_')
def updateInstances_(self, conn, domain_ids):
for domain_id in domain_ids:
if not domain_id in self._instances:
instance = Instance(conn, domain_id)
self._instances[domain_id] = instance
- logging.debug(_('Found instance: %s'), domain_id)
+ LOG.debug(_('Found instance: %s'), domain_id)
for key in self._instances.keys():
instance = self._instances[key]
diff --git a/nova/crypto.py b/nova/crypto.py
index b8405552d..a34b940f5 100644
--- a/nova/crypto.py
+++ b/nova/crypto.py
@@ -24,7 +24,6 @@ Includes root and intermediate CAs, SSH key_pairs and x509 certificates.
import base64
import gettext
import hashlib
-import logging
import os
import shutil
import struct
@@ -39,8 +38,10 @@ gettext.install('nova', unicode=1)
from nova import context
from nova import db
from nova import flags
+from nova import log as logging
+LOG = logging.getLogger("nova.crypto")
FLAGS = flags.FLAGS
flags.DEFINE_string('ca_file', 'cacert.pem', _('Filename of root CA'))
flags.DEFINE_string('key_file',
@@ -254,7 +255,7 @@ def _sign_csr(csr_text, ca_folder):
csrfile = open(inbound, "w")
csrfile.write(csr_text)
csrfile.close()
- logging.debug(_("Flags path: %s") % ca_folder)
+ LOG.debug(_("Flags path: %s"), ca_folder)
start = os.getcwd()
# Change working dir to CA
os.chdir(ca_folder)
diff --git a/nova/db/api.py b/nova/db/api.py
index ee4c521a0..64fee590b 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -83,7 +83,7 @@ def service_get(context, service_id):
def service_get_all(context, disabled=False):
"""Get all service."""
- return IMPL.service_get_all(context, disabled)
+ return IMPL.service_get_all(context, None, disabled)
def service_get_all_by_topic(context, topic):
@@ -783,6 +783,13 @@ def security_group_rule_get_by_security_group(context, security_group_id):
security_group_id)
+def security_group_rule_get_by_security_group_grantee(context,
+ security_group_id):
+ """Get all rules that grant access to the given security group."""
+ return IMPL.security_group_rule_get_by_security_group_grantee(context,
+ security_group_id)
+
+
def security_group_rule_destroy(context, security_group_rule_id):
"""Deletes a security group rule."""
return IMPL.security_group_rule_destroy(context, security_group_rule_id)
diff --git a/nova/db/sqlalchemy/__init__.py b/nova/db/sqlalchemy/__init__.py
index 22aa1cfe6..501373942 100644
--- a/nova/db/sqlalchemy/__init__.py
+++ b/nova/db/sqlalchemy/__init__.py
@@ -19,16 +19,17 @@
"""
SQLAlchemy database backend
"""
-import logging
import time
from sqlalchemy.exc import OperationalError
from nova import flags
+from nova import log as logging
from nova.db.sqlalchemy import models
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.db.sqlalchemy')
for i in xrange(FLAGS.sql_max_retries):
@@ -39,5 +40,6 @@ for i in xrange(FLAGS.sql_max_retries):
models.register_models()
break
except OperationalError:
- logging.exception(_("Data store is unreachable."
- " Trying again in %d seconds.") % FLAGS.sql_retry_interval)
+ LOG.exception(_("Data store %s is unreachable."
+ " Trying again in %d seconds."),
+ FLAGS.sql_connection, FLAGS.sql_retry_interval)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index aa0306eb4..51c642942 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -135,12 +135,16 @@ def service_get(context, service_id, session=None):
@require_admin_context
-def service_get_all(context, disabled=False):
- session = get_session()
- return session.query(models.Service).\
- filter_by(deleted=False).\
+def service_get_all(context, session=None, disabled=False):
+ if not session:
+ session = get_session()
+
+ result = session.query(models.Service).\
+ filter_by(deleted=can_read_deleted(context)).\
filter_by(disabled=disabled).\
all()
+ return result
+
@require_admin_context
def service_get_all_by_topic(context, topic):
@@ -667,7 +671,7 @@ def instance_get(context, instance_id, session=None):
if is_admin_context(context):
result = session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload('security_groups')).\
+ options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\
@@ -675,7 +679,7 @@ def instance_get(context, instance_id, session=None):
elif is_user_context(context):
result = session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
- options(joinedload('security_groups')).\
+ options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
filter_by(project_id=context.project_id).\
filter_by(id=instance_id).\
@@ -857,12 +861,9 @@ def instance_action_create(context, values):
def instance_get_actions(context, instance_id):
"""Return the actions associated to the given instance id"""
session = get_session()
- actions = {}
- for action in session.query(models.InstanceActions).\
+ return session.query(models.InstanceActions).\
filter_by(instance_id=instance_id).\
- all():
- actions[action.action] = action.error
- return actions
+ all()
###################
@@ -1599,6 +1600,44 @@ def security_group_rule_get(context, security_group_rule_id, session=None):
@require_context
+def security_group_rule_get_by_security_group(context, security_group_id,
+ session=None):
+ if not session:
+ session = get_session()
+ if is_admin_context(context):
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=can_read_deleted(context)).\
+ filter_by(parent_group_id=security_group_id).\
+ all()
+ else:
+ # TODO(vish): Join to group and check for project_id
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=False).\
+ filter_by(parent_group_id=security_group_id).\
+ all()
+ return result
+
+
+@require_context
+def security_group_rule_get_by_security_group_grantee(context,
+ security_group_id,
+ session=None):
+ if not session:
+ session = get_session()
+ if is_admin_context(context):
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=can_read_deleted(context)).\
+ filter_by(group_id=security_group_id).\
+ all()
+ else:
+ result = session.query(models.SecurityGroupIngressRule).\
+ filter_by(deleted=False).\
+ filter_by(group_id=security_group_id).\
+ all()
+ return result
+
+
+@require_context
def security_group_rule_create(context, values):
security_group_rule_ref = models.SecurityGroupIngressRule()
security_group_rule_ref.update(values)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 1ffb9298f..05ddbeb72 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -224,6 +224,8 @@ class Instance(BASE, NovaBase):
display_name = Column(String(255))
display_description = Column(String(255))
+ locked = Column(Boolean)
+
# TODO(vish): see Ewan's email about state improvements, probably
# should be in a driver base class or some such
# vmstate_state = running, halted, suspended, paused
diff --git a/nova/exception.py b/nova/exception.py
index 277033e0f..7680e534a 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -21,9 +21,8 @@ Nova base exception handling, including decorator for re-raising
Nova-type exceptions. SHOULD include dedicated exception logging.
"""
-import logging
-import sys
-import traceback
+from nova import log as logging
+LOG = logging.getLogger('nova.exception')
class ProcessExecutionError(IOError):
@@ -84,7 +83,7 @@ def wrap_exception(f):
except Exception, e:
if not isinstance(e, Error):
#exc_type, exc_value, exc_traceback = sys.exc_info()
- logging.exception(_('Uncaught exception'))
+ LOG.exception(_('Uncaught exception'))
#logging.error(traceback.extract_stack(exc_traceback))
raise Error(str(e))
raise
diff --git a/nova/fakerabbit.py b/nova/fakerabbit.py
index 79d8b894d..7c2d7177b 100644
--- a/nova/fakerabbit.py
+++ b/nova/fakerabbit.py
@@ -18,12 +18,16 @@
"""Based a bit on the carrot.backeds.queue backend... but a lot better."""
-import logging
import Queue as queue
from carrot.backends import base
from eventlet import greenthread
+from nova import log as logging
+
+
+LOG = logging.getLogger("nova.fakerabbit")
+
EXCHANGES = {}
QUEUES = {}
@@ -41,12 +45,12 @@ class Exchange(object):
self._routes = {}
def publish(self, message, routing_key=None):
- logging.debug(_('(%s) publish (key: %s) %s'),
- self.name, routing_key, message)
+ LOG.debug(_('(%s) publish (key: %s) %s'),
+ self.name, routing_key, message)
routing_key = routing_key.split('.')[0]
if routing_key in self._routes:
for f in self._routes[routing_key]:
- logging.debug(_('Publishing to route %s'), f)
+ LOG.debug(_('Publishing to route %s'), f)
f(message, routing_key=routing_key)
def bind(self, callback, routing_key):
@@ -76,19 +80,19 @@ class Backend(base.BaseBackend):
def queue_declare(self, queue, **kwargs):
global QUEUES
if queue not in QUEUES:
- logging.debug(_('Declaring queue %s'), queue)
+ LOG.debug(_('Declaring queue %s'), queue)
QUEUES[queue] = Queue(queue)
def exchange_declare(self, exchange, type, *args, **kwargs):
global EXCHANGES
if exchange not in EXCHANGES:
- logging.debug(_('Declaring exchange %s'), exchange)
+ LOG.debug(_('Declaring exchange %s'), exchange)
EXCHANGES[exchange] = Exchange(exchange, type)
def queue_bind(self, queue, exchange, routing_key, **kwargs):
global EXCHANGES
global QUEUES
- logging.debug(_('Binding %s to %s with key %s'),
+ LOG.debug(_('Binding %s to %s with key %s'),
queue, exchange, routing_key)
EXCHANGES[exchange].bind(QUEUES[queue].push, routing_key)
@@ -113,7 +117,7 @@ class Backend(base.BaseBackend):
content_type=content_type,
content_encoding=content_encoding)
message.result = True
- logging.debug(_('Getting from %s: %s'), queue, message)
+ LOG.debug(_('Getting from %s: %s'), queue, message)
return message
def prepare_message(self, message_data, delivery_mode,
diff --git a/nova/flags.py b/nova/flags.py
index 4e71d2152..87f95a25d 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -29,8 +29,6 @@ import sys
import gflags
-from nova import utils
-
class FlagValues(gflags.FlagValues):
"""Extension of gflags.FlagValues that allows undefined and runtime flags.
@@ -202,10 +200,22 @@ def DECLARE(name, module_string, flag_values=FLAGS):
"%s not defined by %s" % (name, module_string))
+def _get_my_ip():
+ """Returns the actual ip of the local machine."""
+ try:
+ csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ csock.connect(('8.8.8.8', 80))
+ (addr, port) = csock.getsockname()
+ csock.close()
+ return addr
+ except socket.gaierror as ex:
+ return "127.0.0.1"
+
+
# __GLOBAL FLAGS ONLY__
# Define any app-specific flags in their own files, docs at:
-# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#39
-
+# http://code.google.com/p/python-gflags/source/browse/trunk/gflags.py#a9
+DEFINE_string('my_ip', _get_my_ip(), 'host ip address')
DEFINE_list('region_list',
[],
'list of region=url pairs separated by commas')
@@ -213,10 +223,10 @@ DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('glance_port', 9292, 'glance port')
-DEFINE_string('glance_host', utils.get_my_ip(), 'glance host')
+DEFINE_string('glance_host', '$my_ip', 'glance host')
DEFINE_integer('s3_port', 3333, 's3 port')
-DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
-DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
+DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)')
+DEFINE_string('s3_dmz', '$my_ip', 's3 dmz ip (for instances)')
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
DEFINE_string('scheduler_topic', 'scheduler',
'the topic scheduler nodes listen on')
@@ -236,8 +246,8 @@ DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval')
DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts')
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
DEFINE_string('ec2_prefix', 'http', 'prefix for ec2')
-DEFINE_string('cc_host', utils.get_my_ip(), 'ip of api server')
-DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
+DEFINE_string('cc_host', '$my_ip', 'ip of api server')
+DEFINE_string('cc_dmz', '$my_ip', 'internal ip of api server')
DEFINE_integer('cc_port', 8773, 'cloud controller port')
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
diff --git a/nova/image/glance.py b/nova/image/glance.py
index cb3936df1..a3a2f4308 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -19,19 +19,17 @@
import httplib
import json
-import logging
import urlparse
-import webob.exc
-
-from nova import utils
-from nova import flags
from nova import exception
-import nova.image.service
+from nova import flags
+from nova import log as logging
+from nova.image import service
-FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.image.glance')
+FLAGS = flags.FLAGS
flags.DEFINE_string('glance_teller_address', 'http://127.0.0.1',
'IP address or URL where Glance\'s Teller service resides')
flags.DEFINE_string('glance_teller_port', '9191',
@@ -77,8 +75,8 @@ class ParallaxClient(object):
data = json.loads(res.read())['images']
return data
else:
- logging.warn(_("Parallax returned HTTP error %d from "
- "request for /images"), res.status_int)
+ LOG.warn(_("Parallax returned HTTP error %d from "
+ "request for /images"), res.status_int)
return []
finally:
c.close()
@@ -96,8 +94,8 @@ class ParallaxClient(object):
data = json.loads(res.read())['images']
return data
else:
- logging.warn(_("Parallax returned HTTP error %d from "
- "request for /images/detail"), res.status_int)
+ LOG.warn(_("Parallax returned HTTP error %d from "
+ "request for /images/detail"), res.status_int)
return []
finally:
c.close()
@@ -165,7 +163,7 @@ class ParallaxClient(object):
c.close()
-class GlanceImageService(nova.image.service.BaseImageService):
+class GlanceImageService(service.BaseImageService):
"""Provides storage and retrieval of disk image objects within Glance."""
def __init__(self):
diff --git a/nova/log.py b/nova/log.py
new file mode 100644
index 000000000..c1428c051
--- /dev/null
+++ b/nova/log.py
@@ -0,0 +1,254 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""
+Nova logging handler.
+
+This module adds to logging functionality by adding the option to specify
+a context object when calling the various log methods. If the context object
+is not specified, default formatting is used.
+
+It also allows setting of formatting information through flags.
+"""
+
+
+import cStringIO
+import json
+import logging
+import logging.handlers
+import traceback
+
+from nova import flags
+from nova import version
+
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('logging_context_format_string',
+ '(%(name)s %(nova_version)s): %(levelname)s '
+ '[%(request_id)s %(user)s '
+ '%(project)s] %(message)s',
+ 'format string to use for log messages')
+
+flags.DEFINE_string('logging_default_format_string',
+ '(%(name)s %(nova_version)s): %(levelname)s [N/A] '
+ '%(message)s',
+ 'format string to use for log messages')
+
+flags.DEFINE_string('logging_debug_format_suffix',
+ 'from %(processName)s (pid=%(process)d) %(funcName)s'
+ ' %(pathname)s:%(lineno)d',
+ 'data to append to log format when level is DEBUG')
+
+flags.DEFINE_string('logging_exception_prefix',
+ '(%(name)s): TRACE: ',
+ 'prefix each line of exception output with this format')
+
+flags.DEFINE_list('default_log_levels',
+ ['amqplib=WARN',
+ 'sqlalchemy=WARN',
+ 'eventlet.wsgi.server=WARN'],
+ 'list of logger=LEVEL pairs')
+
+flags.DEFINE_bool('use_syslog', False, 'output to syslog')
+flags.DEFINE_string('logfile', None, 'output to named file')
+
+
+# A list of things we want to replicate from logging.
+# levels
+CRITICAL = logging.CRITICAL
+FATAL = logging.FATAL
+ERROR = logging.ERROR
+WARNING = logging.WARNING
+WARN = logging.WARN
+INFO = logging.INFO
+DEBUG = logging.DEBUG
+NOTSET = logging.NOTSET
+# methods
+getLogger = logging.getLogger
+debug = logging.debug
+info = logging.info
+warning = logging.warning
+warn = logging.warn
+error = logging.error
+exception = logging.exception
+critical = logging.critical
+log = logging.log
+# handlers
+StreamHandler = logging.StreamHandler
+FileHandler = logging.FileHandler
+# logging.SysLogHandler is nicer than logging.logging.handler.SysLogHandler.
+SysLogHandler = logging.handlers.SysLogHandler
+
+
+# our new audit level
+AUDIT = logging.INFO + 1
+logging.addLevelName(AUDIT, 'AUDIT')
+
+
+def _dictify_context(context):
+ if context == None:
+ return None
+ if not isinstance(context, dict) \
+ and getattr(context, 'to_dict', None):
+ context = context.to_dict()
+ return context
+
+
+def basicConfig():
+ logging.basicConfig()
+ for handler in logging.root.handlers:
+ handler.setFormatter(_formatter)
+ if FLAGS.verbose:
+ logging.root.setLevel(logging.DEBUG)
+ if FLAGS.use_syslog:
+ syslog = SysLogHandler(address='/dev/log')
+ syslog.setFormatter(_formatter)
+ logging.root.addHandler(syslog)
+ if FLAGS.logfile:
+ logfile = FileHandler(FLAGS.logfile)
+ logfile.setFormatter(_formatter)
+ logging.root.addHandler(logfile)
+
+
+class NovaLogger(logging.Logger):
+ """
+ NovaLogger manages request context and formatting.
+
+ This becomes the class that is instanciated by logging.getLogger.
+ """
+ def __init__(self, name, level=NOTSET):
+ level_name = self._get_level_from_flags(name, FLAGS)
+ level = globals()[level_name]
+ logging.Logger.__init__(self, name, level)
+
+ def _get_level_from_flags(self, name, FLAGS):
+ # if exactly "nova", or a child logger, honor the verbose flag
+ if (name == "nova" or name.startswith("nova.")) and FLAGS.verbose:
+ return 'DEBUG'
+ for pair in FLAGS.default_log_levels:
+ logger, _sep, level = pair.partition('=')
+ # NOTE(todd): if we set a.b, we want a.b.c to have the same level
+ # (but not a.bc, so we check the dot)
+ if name == logger:
+ return level
+ if name.startswith(logger) and name[len(logger)] == '.':
+ return level
+ return 'INFO'
+
+ def _log(self, level, msg, args, exc_info=None, extra=None, context=None):
+ """Extract context from any log call"""
+ if not extra:
+ extra = {}
+ if context:
+ extra.update(_dictify_context(context))
+ extra.update({"nova_version": version.version_string_with_vcs()})
+ logging.Logger._log(self, level, msg, args, exc_info, extra)
+
+ def addHandler(self, handler):
+ """Each handler gets our custom formatter"""
+ handler.setFormatter(_formatter)
+ logging.Logger.addHandler(self, handler)
+
+ def audit(self, msg, *args, **kwargs):
+ """Shortcut for our AUDIT level"""
+ if self.isEnabledFor(AUDIT):
+ self._log(AUDIT, msg, args, **kwargs)
+
+ def exception(self, msg, *args, **kwargs):
+ """Logging.exception doesn't handle kwargs, so breaks context"""
+ if not kwargs.get('exc_info'):
+ kwargs['exc_info'] = 1
+ self.error(msg, *args, **kwargs)
+ # NOTE(todd): does this really go here, or in _log ?
+ extra = kwargs.get('extra')
+ if not extra:
+ return
+ env = extra.get('environment')
+ if env:
+ env = env.copy()
+ for k in env.keys():
+ if not isinstance(env[k], str):
+ env.pop(k)
+ message = "Environment: %s" % json.dumps(env)
+ kwargs.pop('exc_info')
+ self.error(message, **kwargs)
+
+logging.setLoggerClass(NovaLogger)
+
+
+class NovaRootLogger(NovaLogger):
+ pass
+
+if not isinstance(logging.root, NovaRootLogger):
+ logging.root = NovaRootLogger("nova.root", WARNING)
+ NovaLogger.root = logging.root
+ NovaLogger.manager.root = logging.root
+
+
+class NovaFormatter(logging.Formatter):
+ """
+ A nova.context.RequestContext aware formatter configured through flags.
+
+ The flags used to set format strings are: logging_context_foramt_string
+ and logging_default_format_string. You can also specify
+ logging_debug_format_suffix to append extra formatting if the log level is
+ debug.
+
+ For information about what variables are available for the formatter see:
+ http://docs.python.org/library/logging.html#formatter
+ """
+
+ def format(self, record):
+ """Uses contextstring if request_id is set, otherwise default"""
+ if record.__dict__.get('request_id', None):
+ self._fmt = FLAGS.logging_context_format_string
+ else:
+ self._fmt = FLAGS.logging_default_format_string
+ if record.levelno == logging.DEBUG \
+ and FLAGS.logging_debug_format_suffix:
+ self._fmt += " " + FLAGS.logging_debug_format_suffix
+ # Cache this on the record, Logger will respect our formated copy
+ if record.exc_info:
+ record.exc_text = self.formatException(record.exc_info, record)
+ return logging.Formatter.format(self, record)
+
+ def formatException(self, exc_info, record=None):
+ """Format exception output with FLAGS.logging_exception_prefix"""
+ if not record:
+ return logging.Formatter.formatException(self, exc_info)
+ stringbuffer = cStringIO.StringIO()
+ traceback.print_exception(exc_info[0], exc_info[1], exc_info[2],
+ None, stringbuffer)
+ lines = stringbuffer.getvalue().split("\n")
+ stringbuffer.close()
+ formatted_lines = []
+ for line in lines:
+ pl = FLAGS.logging_exception_prefix % record.__dict__
+ fl = "%s%s" % (pl, line)
+ formatted_lines.append(fl)
+ return "\n".join(formatted_lines)
+
+_formatter = NovaFormatter()
+
+
+def audit(msg, *args, **kwargs):
+ """Shortcut for logging to root log with sevrity 'AUDIT'."""
+ if len(logging.root.handlers) == 0:
+ basicConfig()
+ logging.root.log(AUDIT, msg, *args, **kwargs)
diff --git a/nova/network/api.py b/nova/network/api.py
index cbd912047..bf43acb51 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -20,15 +20,15 @@
Handles all requests relating to instances (guest vms).
"""
-import logging
-
from nova import db
from nova import flags
+from nova import log as logging
from nova import quota
from nova import rpc
from nova.db import base
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.network')
class API(base.Base):
@@ -36,7 +36,7 @@ class API(base.Base):
def allocate_floating_ip(self, context):
if quota.allowed_floating_ips(context, 1) < 1:
- logging.warn(_("Quota exceeeded for %s, tried to allocate "
+ LOG.warn(_("Quota exceeeded for %s, tried to allocate "
"address"),
context.project_id)
raise quota.QuotaError(_("Address quota exceeded. You cannot "
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 931a89554..3743fc7e8 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -17,16 +17,17 @@
Implements vlans, bridges, and iptables rules using linux utilities.
"""
-import logging
import os
-# TODO(ja): does the definition of network_path belong here?
-
from nova import db
from nova import flags
+from nova import log as logging
from nova import utils
+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))
@@ -45,7 +46,7 @@ flags.DEFINE_string('vlan_interface', 'eth0',
'network device for vlans')
flags.DEFINE_string('dhcpbridge', _bin_file('nova-dhcpbridge'),
'location of nova-dhcpbridge')
-flags.DEFINE_string('routing_source_ip', utils.get_my_ip(),
+flags.DEFINE_string('routing_source_ip', '$my_ip',
'Public IP of network host')
flags.DEFINE_bool('use_nova_chains', False,
'use the nova_ routing chains instead of default')
@@ -172,7 +173,7 @@ def ensure_vlan(vlan_num):
"""Create a vlan unless it already exists"""
interface = "vlan%s" % vlan_num
if not _device_exists(interface):
- logging.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 %s %s" % (FLAGS.vlan_interface, vlan_num))
_execute("sudo ifconfig %s up" % interface)
@@ -182,7 +183,7 @@ def ensure_vlan(vlan_num):
def ensure_bridge(bridge, interface, net_attrs=None):
"""Create a bridge unless it already exists"""
if not _device_exists(bridge):
- logging.debug(_("Starting Bridge interface for %s"), interface)
+ LOG.debug(_("Starting Bridge interface for %s"), interface)
_execute("sudo brctl addbr %s" % bridge)
_execute("sudo brctl setfd %s 0" % bridge)
# _execute("sudo brctl setageing %s 10" % bridge)
@@ -208,6 +209,8 @@ def ensure_bridge(bridge, interface, net_attrs=None):
_confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge)
_confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge)
+ _execute("sudo iptables -N nova-local", check_exit_code=False)
+ _confirm_rule("FORWARD", "-j nova-local")
def get_dhcp_hosts(context, network_id):
@@ -248,9 +251,9 @@ def update_dhcp(context, network_id):
_execute('sudo kill -HUP %d' % pid)
return
except Exception as exc: # pylint: disable-msg=W0703
- logging.debug(_("Hupping dnsmasq threw %s"), exc)
+ LOG.debug(_("Hupping dnsmasq threw %s"), exc)
else:
- logging.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,
@@ -270,7 +273,7 @@ def _host_dhcp(fixed_ip_ref):
def _execute(cmd, *args, **kwargs):
"""Wrapper around utils._execute for fake_network"""
if FLAGS.fake_network:
- logging.debug("FAKE NET: %s", cmd)
+ LOG.debug("FAKE NET: %s", cmd)
return "fake", 0
else:
return utils.execute(cmd, *args, **kwargs)
@@ -328,7 +331,7 @@ def _stop_dnsmasq(network):
try:
_execute('sudo kill -TERM %d' % pid)
except Exception as exc: # pylint: disable-msg=W0703
- logging.debug(_("Killing dnsmasq threw %s"), exc)
+ LOG.debug(_("Killing dnsmasq threw %s"), exc)
def _dhcp_file(bridge, kind):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 16aa8f895..c75ecc671 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -45,7 +45,6 @@ topologies. All of the network commands are issued to a subclass of
"""
import datetime
-import logging
import math
import socket
@@ -55,11 +54,13 @@ 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 rpc
+LOG = logging.getLogger("nova.network.manager")
FLAGS = flags.FLAGS
flags.DEFINE_string('flat_network_bridge', 'br100',
'Bridge for simple network instances')
@@ -73,7 +74,7 @@ flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2',
'Dhcp start for FlatDhcp')
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
flags.DEFINE_integer('num_networks', 1000, 'Number of networks to support')
-flags.DEFINE_string('vpn_ip', utils.get_my_ip(),
+flags.DEFINE_string('vpn_ip', '$my_ip',
'Public IP for the cloudpipe VPN servers')
flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
flags.DEFINE_integer('network_size', 256,
@@ -131,7 +132,7 @@ class NetworkManager(manager.Manager):
def set_network_host(self, context, network_id):
"""Safely sets the host of the network."""
- logging.debug(_("setting network host"))
+ LOG.debug(_("setting network host"), context=context)
host = self.db.network_set_host(context,
network_id,
self.host)
@@ -186,7 +187,7 @@ class NetworkManager(manager.Manager):
def lease_fixed_ip(self, context, mac, address):
"""Called by dhcp-bridge when ip is leased."""
- logging.debug("Leasing IP %s", address)
+ 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:
@@ -201,12 +202,12 @@ class NetworkManager(manager.Manager):
{'leased': True,
'updated_at': now})
if not fixed_ip_ref['allocated']:
- logging.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."""
- logging.debug("Releasing IP %s", address)
+ 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:
@@ -216,7 +217,8 @@ class NetworkManager(manager.Manager):
raise exception.Error(_("IP %s released from bad mac %s vs %s") %
(address, instance_ref['mac_address'], mac))
if not fixed_ip_ref['leased']:
- logging.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'],
{'leased': False})
@@ -437,7 +439,7 @@ class VlanManager(NetworkManager):
self.host,
time)
if num:
- logging.debug(_("Dissassociated %s stale fixed ip(s)"), num)
+ LOG.debug(_("Dissassociated %s stale fixed ip(s)"), num)
def init_host(self):
"""Do any initialization that needs to be run if this is a
diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py
index 52257f69f..bc26fd3c5 100644
--- a/nova/objectstore/handler.py
+++ b/nova/objectstore/handler.py
@@ -39,7 +39,6 @@ S3 client with this module::
import datetime
import json
-import logging
import multiprocessing
import os
import urllib
@@ -54,12 +53,14 @@ from twisted.web import static
from nova import context
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
from nova.auth import manager
from nova.objectstore import bucket
from nova.objectstore import image
+LOG = logging.getLogger('nova.objectstore.handler')
FLAGS = flags.FLAGS
flags.DEFINE_string('s3_listen_host', '', 'Host to listen on.')
@@ -132,9 +133,11 @@ def get_context(request):
request.uri,
headers=request.getAllHeaders(),
check_type='s3')
- return context.RequestContext(user, project)
+ rv = context.RequestContext(user, project)
+ LOG.audit(_("Authenticated request"), context=rv)
+ return rv
except exception.Error as ex:
- logging.debug(_("Authentication Failure: %s"), ex)
+ LOG.debug(_("Authentication Failure: %s"), ex)
raise exception.NotAuthorized()
@@ -176,7 +179,7 @@ class S3(ErrorHandlingResource):
def render_GET(self, request): # pylint: disable-msg=R0201
"""Renders the GET request for a list of buckets as XML"""
- logging.debug('List of buckets requested')
+ LOG.debug(_('List of buckets requested'), context=request.context)
buckets = [b for b in bucket.Bucket.all() \
if b.is_authorized(request.context)]
@@ -203,7 +206,7 @@ class BucketResource(ErrorHandlingResource):
def render_GET(self, request):
"Returns the keys for the bucket resource"""
- logging.debug("List keys for bucket %s", self.name)
+ LOG.debug(_("List keys for bucket %s"), self.name)
try:
bucket_object = bucket.Bucket(self.name)
@@ -211,6 +214,8 @@ class BucketResource(ErrorHandlingResource):
return error.NoResource(message="No such bucket").render(request)
if not bucket_object.is_authorized(request.context):
+ LOG.audit(_("Unauthorized attempt to access bucket %s"),
+ self.name, context=request.context)
raise exception.NotAuthorized()
prefix = get_argument(request, "prefix", u"")
@@ -227,8 +232,8 @@ class BucketResource(ErrorHandlingResource):
def render_PUT(self, request):
"Creates the bucket resource"""
- logging.debug(_("Creating bucket %s"), self.name)
- logging.debug("calling bucket.Bucket.create(%r, %r)",
+ LOG.debug(_("Creating bucket %s"), self.name)
+ LOG.debug("calling bucket.Bucket.create(%r, %r)",
self.name,
request.context)
bucket.Bucket.create(self.name, request.context)
@@ -237,10 +242,12 @@ class BucketResource(ErrorHandlingResource):
def render_DELETE(self, request):
"""Deletes the bucket resource"""
- logging.debug(_("Deleting bucket %s"), self.name)
+ LOG.debug(_("Deleting bucket %s"), self.name)
bucket_object = bucket.Bucket(self.name)
if not bucket_object.is_authorized(request.context):
+ LOG.audit(_("Unauthorized attempt to delete bucket %s"),
+ self.name, context=request.context)
raise exception.NotAuthorized()
bucket_object.delete()
@@ -261,11 +268,12 @@ class ObjectResource(ErrorHandlingResource):
Raises NotAuthorized if user in request context is not
authorized to delete the object.
"""
- logging.debug(_("Getting object: %s / %s"),
- self.bucket.name,
- self.name)
+ LOG.debug(_("Getting object: %s / %s"), self.bucket.name, self.name)
if not self.bucket.is_authorized(request.context):
+ LOG.audit(_("Unauthorized attempt to get object %s from bucket "
+ "%s"), self.name, self.bucket.name,
+ context=request.context)
raise exception.NotAuthorized()
obj = self.bucket[urllib.unquote(self.name)]
@@ -281,11 +289,12 @@ class ObjectResource(ErrorHandlingResource):
Raises NotAuthorized if user in request context is not
authorized to delete the object.
"""
- logging.debug(_("Putting object: %s / %s"),
- self.bucket.name,
- self.name)
+ LOG.debug(_("Putting object: %s / %s"), self.bucket.name, self.name)
if not self.bucket.is_authorized(request.context):
+ LOG.audit(_("Unauthorized attempt to upload object %s to bucket "
+ "%s"),
+ self.name, self.bucket.name, context=request.context)
raise exception.NotAuthorized()
key = urllib.unquote(self.name)
@@ -302,11 +311,13 @@ class ObjectResource(ErrorHandlingResource):
authorized to delete the object.
"""
- logging.debug(_("Deleting object: %s / %s"),
- self.bucket.name,
- self.name)
+ LOG.debug(_("Deleting object: %s / %s"), self.bucket.name, self.name,
+ context=request.context)
if not self.bucket.is_authorized(request.context):
+ LOG.audit("Unauthorized attempt to delete object %s from "
+ "bucket %s", self.name, self.bucket.name,
+ context=request.context)
raise exception.NotAuthorized()
del self.bucket[urllib.unquote(self.name)]
@@ -379,13 +390,21 @@ class ImagesResource(resource.Resource):
image_path = os.path.join(FLAGS.images_path, image_id)
if not image_path.startswith(FLAGS.images_path) or \
os.path.exists(image_path):
+ LOG.audit(_("Not authorized to upload image: invalid directory "
+ "%s"),
+ image_path, context=request.context)
raise exception.NotAuthorized()
bucket_object = bucket.Bucket(image_location.split("/")[0])
if not bucket_object.is_authorized(request.context):
+ LOG.audit(_("Not authorized to upload image: unauthorized "
+ "bucket %s"), bucket_object.name,
+ context=request.context)
raise exception.NotAuthorized()
+ LOG.audit(_("Starting image upload: %s"), image_id,
+ context=request.context)
p = multiprocessing.Process(target=image.Image.register_aws_image,
args=(image_id, image_location, request.context))
p.start()
@@ -398,17 +417,21 @@ class ImagesResource(resource.Resource):
image_id = get_argument(request, 'image_id', u'')
image_object = image.Image(image_id)
if not image_object.is_authorized(request.context):
- logging.debug(_("not authorized for render_POST in images"))
+ LOG.audit(_("Not authorized to update attributes of image %s"),
+ image_id, context=request.context)
raise exception.NotAuthorized()
operation = get_argument(request, 'operation', u'')
if operation:
# operation implies publicity toggle
- logging.debug(_("handling publicity toggle"))
- image_object.set_public(operation == 'add')
+ newstatus = (operation == 'add')
+ LOG.audit(_("Toggling publicity flag of image %s %r"), image_id,
+ newstatus, context=request.context)
+ image_object.set_public(newstatus)
else:
# other attributes imply update
- logging.debug(_("update user fields"))
+ LOG.audit(_("Updating user fields on image %s"), image_id,
+ context=request.context)
clean_args = {}
for arg in request.args.keys():
clean_args[arg] = request.args[arg][0]
@@ -421,9 +444,12 @@ class ImagesResource(resource.Resource):
image_object = image.Image(image_id)
if not image_object.is_authorized(request.context):
+ LOG.audit(_("Unauthorized attempt to delete image %s"),
+ image_id, context=request.context)
raise exception.NotAuthorized()
image_object.delete()
+ LOG.audit(_("Deleted image: %s"), image_id, context=request.context)
request.setResponseCode(204)
return ''
diff --git a/nova/rpc.py b/nova/rpc.py
index 844088348..49b11602b 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -22,7 +22,6 @@ No fan-out support yet.
"""
import json
-import logging
import sys
import time
import traceback
@@ -36,13 +35,12 @@ from nova import context
from nova import exception
from nova import fakerabbit
from nova import flags
+from nova import log as logging
from nova import utils
FLAGS = flags.FLAGS
-
-LOG = logging.getLogger('amqplib')
-LOG.setLevel(logging.DEBUG)
+LOG = logging.getLogger('nova.rpc')
class Connection(carrot_connection.BrokerConnection):
@@ -91,15 +89,16 @@ class Consumer(messaging.Consumer):
self.failed_connection = False
break
except: # Catching all because carrot sucks
- logging.exception(_("AMQP server on %s:%d is unreachable."
- " Trying again in %d seconds.") % (
- FLAGS.rabbit_host,
- FLAGS.rabbit_port,
- FLAGS.rabbit_retry_interval))
+ LOG.exception(_("AMQP server on %s:%d is unreachable."
+ " Trying again in %d seconds.") % (
+ FLAGS.rabbit_host,
+ FLAGS.rabbit_port,
+ FLAGS.rabbit_retry_interval))
self.failed_connection = True
if self.failed_connection:
- logging.exception(_("Unable to connect to AMQP server"
- " after %d tries. Shutting down.") % FLAGS.rabbit_max_retries)
+ LOG.exception(_("Unable to connect to AMQP server "
+ "after %d tries. Shutting down."),
+ FLAGS.rabbit_max_retries)
sys.exit(1)
def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
@@ -116,14 +115,14 @@ class Consumer(messaging.Consumer):
self.declare()
super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
if self.failed_connection:
- logging.error(_("Reconnected to queue"))
+ LOG.error(_("Reconnected to queue"))
self.failed_connection = False
# NOTE(vish): This is catching all errors because we really don't
# exceptions to be logged 10 times a second if some
# persistent failure occurs.
except Exception: # pylint: disable-msg=W0703
if not self.failed_connection:
- logging.exception(_("Failed to fetch message from queue"))
+ LOG.exception(_("Failed to fetch message from queue"))
self.failed_connection = True
def attach_to_eventlet(self):
@@ -193,6 +192,7 @@ class AdapterConsumer(TopicConsumer):
if msg_id:
msg_reply(msg_id, rval, None)
except Exception as e:
+ logging.exception("Exception during message handling")
if msg_id:
msg_reply(msg_id, None, sys.exc_info())
return
@@ -242,8 +242,8 @@ def msg_reply(msg_id, reply=None, failure=None):
if failure:
message = str(failure[1])
tb = traceback.format_exception(*failure)
- logging.error(_("Returning exception %s to caller"), message)
- logging.error(tb)
+ LOG.error(_("Returning exception %s to caller"), message)
+ LOG.error(tb)
failure = (failure[0].__name__, str(failure[1]), tb)
conn = Connection.instance(True)
publisher = DirectPublisher(connection=conn, msg_id=msg_id)
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 44e21f2fd..a4d6dd574 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -21,15 +21,16 @@
Scheduler Service
"""
-import logging
import functools
from nova import db
from nova import flags
+from nova import log as logging
from nova import manager
from nova import rpc
from nova import utils
+LOG = logging.getLogger('nova.scheduler.manager')
FLAGS = flags.FLAGS
flags.DEFINE_string('scheduler_driver',
'nova.scheduler.chance.ChanceScheduler',
@@ -65,4 +66,4 @@ class SchedulerManager(manager.Manager):
db.queue_get_for(context, topic, host),
{"method": method,
"args": kwargs})
- logging.debug(_("Casting to %s %s for %s"), topic, host, method)
+ LOG.debug(_("Casting to %s %s for %s"), topic, host, method)
diff --git a/nova/service.py b/nova/service.py
index d4a6f3839..2998ed3e5 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -21,7 +21,6 @@ Generic Node baseclass for all workers that run on hosts
"""
import inspect
-import logging
import os
import sys
import time
@@ -35,10 +34,10 @@ from sqlalchemy.exc import OperationalError
from nova import context
from nova import db
from nova import exception
+from nova import log as logging
from nova import flags
from nova import rpc
from nova import utils
-from nova.db.sqlalchemy import models
FLAGS = flags.FLAGS
@@ -156,7 +155,7 @@ class Service(object):
report_interval = FLAGS.report_interval
if not periodic_interval:
periodic_interval = FLAGS.periodic_interval
- logging.warn(_("Starting %s node"), topic)
+ logging.audit(_("Starting %s node"), topic)
service_obj = cls(host, binary, topic, manager,
report_interval, periodic_interval)
@@ -210,29 +209,28 @@ class Service(object):
logging.exception(_("model server went away"))
try:
+ # NOTE(vish): This is late-loaded to make sure that the
+ # database is not created before flags have
+ # been loaded.
+ from nova.db.sqlalchemy import models
models.register_models()
except OperationalError:
- logging.exception(_("Data store is unreachable."
- " Trying again in %d seconds.") %
- FLAGS.sql_retry_interval)
+ logging.exception(_("Data store %s is unreachable."
+ " Trying again in %d seconds.") %
+ (FLAGS.sql_connection,
+ FLAGS.sql_retry_interval))
time.sleep(FLAGS.sql_retry_interval)
def serve(*services):
- argv = FLAGS(sys.argv)
+ FLAGS(sys.argv)
+ logging.basicConfig()
if not services:
services = [Service.create()]
name = '_'.join(x.binary for x in services)
- logging.debug("Serving %s" % name)
-
- logging.getLogger('amqplib').setLevel(logging.WARN)
-
- if FLAGS.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
- else:
- logging.getLogger().setLevel(logging.WARNING)
+ logging.debug(_("Serving %s"), name)
logging.debug(_("Full set of FLAGS:"))
for flag in FLAGS:
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 291a0e468..194304e79 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -107,7 +107,7 @@ def stub_out_rate_limiting(stubs):
def stub_out_networking(stubs):
def get_my_ip():
return '127.0.0.1'
- stubs.Set(nova.utils, 'get_my_ip', get_my_ip)
+ stubs.Set(nova.flags, '_get_my_ip', get_my_ip)
def stub_out_compute_api_snapshot(stubs):
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 0f274bd15..f5be9c94f 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -22,7 +22,6 @@ and as a WSGI layer
import json
import datetime
-import logging
import unittest
import stubout
diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py
index ceac17adb..da86e6e11 100644
--- a/nova/tests/objectstore_unittest.py
+++ b/nova/tests/objectstore_unittest.py
@@ -23,7 +23,6 @@ Unittets for S3 objectstore clone.
import boto
import glob
import hashlib
-import logging
import os
import shutil
import tempfile
@@ -63,7 +62,6 @@ class ObjectStoreTestCase(test.TestCase):
self.flags(buckets_path=os.path.join(OSS_TEMPDIR, 'buckets'),
images_path=os.path.join(OSS_TEMPDIR, 'images'),
ca_path=os.path.join(os.path.dirname(__file__), 'CA'))
- logging.getLogger().setLevel(logging.DEBUG)
self.auth_manager = manager.AuthManager()
self.auth_manager.create_user('user1')
diff --git a/nova/tests/test_access.py b/nova/tests/test_access.py
index 58fdea3b5..0929903cf 100644
--- a/nova/tests/test_access.py
+++ b/nova/tests/test_access.py
@@ -17,7 +17,6 @@
# under the License.
import unittest
-import logging
import webob
from nova import context
diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py
index 15d40bc53..35ffffb67 100644
--- a/nova/tests/test_auth.py
+++ b/nova/tests/test_auth.py
@@ -16,17 +16,18 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
from M2Crypto import X509
import unittest
from nova import crypto
from nova import flags
+from nova import log as logging
from nova import test
from nova.auth import manager
from nova.api.ec2 import cloud
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.auth_unittest')
class user_generator(object):
@@ -211,12 +212,12 @@ class AuthManagerTestCase(object):
# NOTE(vish): Setup runs genroot.sh if it hasn't been run
cloud.CloudController().setup()
_key, cert_str = crypto.generate_x509_cert(user.id, project.id)
- logging.debug(cert_str)
+ LOG.debug(cert_str)
full_chain = crypto.fetch_ca(project_id=project.id, chain=True)
int_cert = crypto.fetch_ca(project_id=project.id, chain=False)
cloud_cert = crypto.fetch_ca()
- logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
+ LOG.debug("CA chain:\n\n =====\n%s\n\n=====", full_chain)
signed_cert = X509.load_cert_string(cert_str)
chain_cert = X509.load_cert_string(full_chain)
int_cert = X509.load_cert_string(int_cert)
@@ -331,7 +332,7 @@ class AuthManagerLdapTestCase(AuthManagerTestCase, test.TestCase):
test.TestCase.__init__(self, *args, **kwargs)
import nova.auth.fakeldap as fakeldap
if FLAGS.flush_db:
- logging.info("Flushing datastore")
+ LOG.info("Flushing datastore")
r = fakeldap.Store.instance()
r.flushdb()
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index 21d212df7..afb6a8f1b 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -18,7 +18,6 @@
from base64 import b64decode
import json
-import logging
from M2Crypto import BIO
from M2Crypto import RSA
import os
@@ -31,6 +30,7 @@ from nova import context
from nova import crypto
from nova import db
from nova import flags
+from nova import log as logging
from nova import rpc
from nova import service
from nova import test
@@ -41,6 +41,7 @@ from nova.objectstore import image
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.cloud')
# Temp dirs for working with image attributes through the cloud controller
# (stole this from objectstore_unittest.py)
@@ -56,7 +57,6 @@ class CloudTestCase(test.TestCase):
images_path=IMAGES_PATH)
self.conn = rpc.Connection.instance()
- logging.getLogger().setLevel(logging.DEBUG)
# set up our cloud
self.cloud = cloud.CloudController()
@@ -133,7 +133,6 @@ class CloudTestCase(test.TestCase):
db.volume_destroy(self.context, vol1['id'])
db.volume_destroy(self.context, vol2['id'])
-
def test_describe_availability_zones(self):
"""Makes sure describe_availability_zones works and filters results."""
service1 = db.service_create(self.context, {'host': 'host1_describe_zones',
@@ -151,6 +150,34 @@ class CloudTestCase(test.TestCase):
db.service_destroy(self.context, service1['id'])
db.service_destroy(self.context, service2['id'])
+
+ def test_describe_instances(self):
+ """Makes sure describe_instances works and filters results."""
+ inst1 = db.instance_create(self.context, {'reservation_id': 'a', 'host': 'host1'})
+ inst2 = db.instance_create(self.context, {'reservation_id': 'a', 'host': 'host2'})
+ compute1 = db.service_create(self.context, {'host': 'host1',
+ 'availability_zone': 'zone1',
+ 'topic': "compute"})
+ compute2 = db.service_create(self.context, {'host': 'host2',
+ 'availability_zone': 'zone2',
+ 'topic': "compute"})
+ result = self.cloud.describe_instances(self.context)
+ result = result['reservationSet'][0]
+ self.assertEqual(len(result['instancesSet']), 2)
+ instance_id = cloud.id_to_ec2_id(inst2['id'])
+ result = self.cloud.describe_instances(self.context,
+ instance_id=[instance_id])
+ result = result['reservationSet'][0]
+ self.assertEqual(len(result['instancesSet']), 1)
+ self.assertEqual(result['instancesSet'][0]['instanceId'],
+ instance_id)
+ self.assertEqual(result['instancesSet'][0]\
+ ['placement']['availabilityZone'], 'zone2')
+ db.instance_destroy(self.context, inst1['id'])
+ db.instance_destroy(self.context, inst2['id'])
+ db.service_destroy(self.context, compute1['id'])
+ db.service_destroy(self.context, compute2['id'])
+
def test_console_output(self):
image_id = FLAGS.default_image
@@ -160,7 +187,6 @@ class CloudTestCase(test.TestCase):
'instance_type': instance_type,
'max_count': max_count}
rv = self.cloud.run_instances(self.context, **kwargs)
- print rv
instance_id = rv['instancesSet'][0]['instanceId']
output = self.cloud.get_console_output(context=self.context,
instance_id=[instance_id])
@@ -198,7 +224,7 @@ class CloudTestCase(test.TestCase):
def test_run_instances(self):
if FLAGS.connection_type == 'fake':
- logging.debug("Can't test instances without a real virtual env.")
+ LOG.debug(_("Can't test instances without a real virtual env."))
return
image_id = FLAGS.default_image
instance_type = FLAGS.default_instance_type
@@ -210,25 +236,25 @@ class CloudTestCase(test.TestCase):
# TODO: check for proper response
instance_id = rv['reservationSet'][0].keys()[0]
instance = rv['reservationSet'][0][instance_id][0]
- logging.debug("Need to watch instance %s until it's running..." %
- instance['instance_id'])
+ LOG.debug(_("Need to watch instance %s until it's running..."),
+ instance['instance_id'])
while True:
greenthread.sleep(1)
info = self.cloud._get_instance(instance['instance_id'])
- logging.debug(info['state'])
+ LOG.debug(info['state'])
if info['state'] == power_state.RUNNING:
break
self.assert_(rv)
- if connection_type != 'fake':
+ if FLAGS.connection_type != 'fake':
time.sleep(45) # Should use boto for polling here
for reservations in rv['reservationSet']:
# for res_id in reservations.keys():
- # logging.debug(reservations[res_id])
+ # LOG.debug(reservations[res_id])
# for instance in reservations[res_id]:
for instance in reservations[reservations.keys()[0]]:
instance_id = instance['instance_id']
- logging.debug("Terminating instance %s" % instance_id)
+ LOG.debug(_("Terminating instance %s"), instance_id)
rv = self.compute.terminate_instance(instance_id)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 1d527b8f0..1d407c5a3 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -20,25 +20,25 @@ Tests For Compute
"""
import datetime
-import logging
from nova import compute
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 test
from nova import utils
from nova.auth import manager
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.compute')
class ComputeTestCase(test.TestCase):
"""Test case for compute"""
def setUp(self):
- logging.getLogger().setLevel(logging.DEBUG)
super(ComputeTestCase, self).setUp()
self.flags(connection_type='fake',
stub_network=True,
@@ -101,13 +101,13 @@ class ComputeTestCase(test.TestCase):
self.compute.run_instance(self.context, instance_id)
instances = db.instance_get_all(context.get_admin_context())
- logging.info(_("Running instances: %s"), instances)
+ LOG.info(_("Running instances: %s"), instances)
self.assertEqual(len(instances), 1)
self.compute.terminate_instance(self.context, instance_id)
instances = db.instance_get_all(context.get_admin_context())
- logging.info(_("After terminating instances: %s"), instances)
+ LOG.info(_("After terminating instances: %s"), instances)
self.assertEqual(len(instances), 0)
def test_run_terminate_timestamps(self):
@@ -178,3 +178,22 @@ class ComputeTestCase(test.TestCase):
self.context,
instance_id)
self.compute.terminate_instance(self.context, instance_id)
+
+ def test_lock(self):
+ """ensure locked instance cannot be changed"""
+ instance_id = self._create_instance()
+ self.compute.run_instance(self.context, instance_id)
+
+ non_admin_context = context.RequestContext(None, None, False, False)
+
+ # decorator should return False (fail) with locked nonadmin context
+ self.compute.lock_instance(self.context, instance_id)
+ ret_val = self.compute.reboot_instance(non_admin_context, instance_id)
+ self.assertEqual(ret_val, False)
+
+ # decorator should return None (success) with unlocked nonadmin context
+ self.compute.unlock_instance(self.context, instance_id)
+ ret_val = self.compute.reboot_instance(non_admin_context, instance_id)
+ self.assertEqual(ret_val, None)
+
+ self.compute.terminate_instance(self.context, instance_id)
diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py
new file mode 100644
index 000000000..beb1d97cf
--- /dev/null
+++ b/nova/tests/test_log.py
@@ -0,0 +1,110 @@
+import cStringIO
+
+from nova import context
+from nova import log
+from nova import test
+
+
+def _fake_context():
+ return context.RequestContext(1, 1)
+
+
+class RootLoggerTestCase(test.TrialTestCase):
+ def setUp(self):
+ super(RootLoggerTestCase, self).setUp()
+ self.log = log.logging.root
+
+ def tearDown(self):
+ super(RootLoggerTestCase, self).tearDown()
+ log.NovaLogger.manager.loggerDict = {}
+
+ def test_is_nova_instance(self):
+ self.assert_(isinstance(self.log, log.NovaLogger))
+
+ def test_name_is_nova_root(self):
+ self.assertEqual("nova.root", self.log.name)
+
+ def test_handlers_have_nova_formatter(self):
+ formatters = []
+ for h in self.log.handlers:
+ f = h.formatter
+ if isinstance(f, log.NovaFormatter):
+ formatters.append(f)
+ self.assert_(formatters)
+ self.assertEqual(len(formatters), len(self.log.handlers))
+
+ def test_handles_context_kwarg(self):
+ self.log.info("foo", context=_fake_context())
+ self.assert_(True) # didn't raise exception
+
+ def test_module_level_methods_handle_context_arg(self):
+ log.info("foo", context=_fake_context())
+ self.assert_(True) # didn't raise exception
+
+ def test_module_level_audit_handles_context_arg(self):
+ log.audit("foo", context=_fake_context())
+ self.assert_(True) # didn't raise exception
+
+
+class NovaFormatterTestCase(test.TrialTestCase):
+ def setUp(self):
+ super(NovaFormatterTestCase, self).setUp()
+ self.flags(logging_context_format_string="HAS CONTEXT "\
+ "[%(request_id)s]: %(message)s",
+ logging_default_format_string="NOCTXT: %(message)s",
+ logging_debug_format_suffix="--DBG")
+ self.log = log.logging.root
+ self.stream = cStringIO.StringIO()
+ handler = log.StreamHandler(self.stream)
+ self.log.addHandler(handler)
+ self.log.setLevel(log.DEBUG)
+
+ def tearDown(self):
+ super(NovaFormatterTestCase, self).tearDown()
+ log.NovaLogger.manager.loggerDict = {}
+
+ def test_uncontextualized_log(self):
+ self.log.info("foo")
+ self.assertEqual("NOCTXT: foo\n", self.stream.getvalue())
+
+ def test_contextualized_log(self):
+ ctxt = _fake_context()
+ self.log.info("bar", context=ctxt)
+ expected = "HAS CONTEXT [%s]: bar\n" % ctxt.request_id
+ self.assertEqual(expected, self.stream.getvalue())
+
+ def test_debugging_log(self):
+ self.log.debug("baz")
+ self.assertEqual("NOCTXT: baz --DBG\n", self.stream.getvalue())
+
+
+class NovaLoggerTestCase(test.TrialTestCase):
+ def setUp(self):
+ super(NovaLoggerTestCase, self).setUp()
+ self.flags(default_log_levels=["nova-test=AUDIT"], verbose=False)
+ self.log = log.getLogger('nova-test')
+
+ def tearDown(self):
+ super(NovaLoggerTestCase, self).tearDown()
+ log.NovaLogger.manager.loggerDict = {}
+
+ def test_has_level_from_flags(self):
+ self.assertEqual(log.AUDIT, self.log.level)
+
+ def test_child_log_has_level_of_parent_flag(self):
+ l = log.getLogger('nova-test.foo')
+ self.assertEqual(log.AUDIT, l.level)
+
+
+class VerboseLoggerTestCase(test.TrialTestCase):
+ def setUp(self):
+ super(VerboseLoggerTestCase, self).setUp()
+ self.flags(default_log_levels=["nova.test=AUDIT"], verbose=True)
+ self.log = log.getLogger('nova.test')
+
+ def tearDown(self):
+ super(VerboseLoggerTestCase, self).tearDown()
+ log.NovaLogger.manager.loggerDict = {}
+
+ def test_will_be_verbose_if_named_nova_and_verbose_flag_set(self):
+ self.assertEqual(log.DEBUG, self.log.level)
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index 96473ac7c..349e20f84 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -20,18 +20,18 @@ Unit Tests for network code
"""
import IPy
import os
-import logging
from nova import context
from nova import db
from nova import exception
from nova import flags
-from nova import service
+from nova import log as logging
from nova import test
from nova import utils
from nova.auth import manager
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
class NetworkTestCase(test.TestCase):
@@ -45,7 +45,6 @@ class NetworkTestCase(test.TestCase):
fake_network=True,
network_size=16,
num_networks=5)
- logging.getLogger().setLevel(logging.DEBUG)
self.manager = manager.AuthManager()
self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
self.projects = []
@@ -328,7 +327,7 @@ def lease_ip(private_ip):
'TESTING': '1',
'FLAGFILE': FLAGS.dhcpbridge_flagfile}
(out, err) = utils.execute(cmd, addl_env=env)
- logging.debug("ISSUE_IP: %s, %s ", out, err)
+ LOG.debug("ISSUE_IP: %s, %s ", out, err)
def release_ip(private_ip):
@@ -344,4 +343,4 @@ def release_ip(private_ip):
'TESTING': '1',
'FLAGFILE': FLAGS.dhcpbridge_flagfile}
(out, err) = utils.execute(cmd, addl_env=env)
- logging.debug("RELEASE_IP: %s, %s ", out, err)
+ LOG.debug("RELEASE_IP: %s, %s ", out, err)
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index b5f9f30ef..9548a8c13 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -16,11 +16,8 @@
# License for the specific language governing permissions and limitations
# under the License.
-import logging
-
from nova import context
from nova import db
-from nova import exception
from nova import flags
from nova import quota
from nova import test
@@ -35,7 +32,6 @@ FLAGS = flags.FLAGS
class QuotaTestCase(test.TestCase):
def setUp(self):
- logging.getLogger().setLevel(logging.DEBUG)
super(QuotaTestCase, self).setUp()
self.flags(connection_type='fake',
quota_instances=2,
diff --git a/nova/tests/test_rpc.py b/nova/tests/test_rpc.py
index 6ea2edcab..85593ab46 100644
--- a/nova/tests/test_rpc.py
+++ b/nova/tests/test_rpc.py
@@ -18,15 +18,16 @@
"""
Unit Tests for remote procedure calls using queue
"""
-import logging
from nova import context
from nova import flags
+from nova import log as logging
from nova import rpc
from nova import test
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.rpc')
class RpcTestCase(test.TestCase):
@@ -85,12 +86,12 @@ class RpcTestCase(test.TestCase):
@staticmethod
def echo(context, queue, value):
"""Calls echo in the passed queue"""
- logging.debug("Nested received %s, %s", queue, value)
+ LOG.debug(_("Nested received %s, %s"), queue, value)
ret = rpc.call(context,
queue,
{"method": "echo",
"args": {"value": value}})
- logging.debug("Nested return %s", ret)
+ LOG.debug(_("Nested return %s"), ret)
return value
nested = Nested()
@@ -115,13 +116,13 @@ class TestReceiver(object):
@staticmethod
def echo(context, value):
"""Simply returns whatever value is sent in"""
- logging.debug("Received %s", value)
+ LOG.debug(_("Received %s"), value)
return value
@staticmethod
def context(context, value):
"""Returns dictionary version of context"""
- logging.debug("Received %s", context)
+ LOG.debug(_("Received %s"), context)
return context.to_dict()
@staticmethod
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 4aa489d08..59053f4d0 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -208,8 +208,99 @@ class LibvirtConnTestCase(test.TestCase):
self.manager.delete_user(self.user)
-class NWFilterTestCase(test.TestCase):
+class IptablesFirewallTestCase(test.TestCase):
+ def setUp(self):
+ super(IptablesFirewallTestCase, self).setUp()
+
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('fake', 'fake', 'fake',
+ admin=True)
+ self.project = self.manager.create_project('fake', 'fake', 'fake')
+ self.context = context.RequestContext('fake', 'fake')
+ self.network = utils.import_object(FLAGS.network_manager)
+ self.fw = libvirt_conn.IptablesFirewallDriver()
+
+ def tearDown(self):
+ self.manager.delete_project(self.project)
+ self.manager.delete_user(self.user)
+ super(IptablesFirewallTestCase, self).tearDown()
+
+ def _p(self, *args, **kwargs):
+ if 'iptables-restore' in args:
+ print ' '.join(args), kwargs['stdin']
+ if 'iptables-save' in args:
+ return
+
+ in_rules = [
+ '# Generated by iptables-save v1.4.4 on Mon Dec 6 11:54:13 2010',
+ '*filter',
+ ':INPUT ACCEPT [969615:281627771]',
+ ':FORWARD ACCEPT [0:0]',
+ ':OUTPUT ACCEPT [915599:63811649]',
+ ':nova-block-ipv4 - [0:0]',
+ '-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT ',
+ '-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT ',
+ '-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT ',
+ '-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT ',
+ '-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED'
+ ',ESTABLISHED -j ACCEPT ',
+ '-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT ',
+ '-A FORWARD -i virbr0 -o virbr0 -j ACCEPT ',
+ '-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable ',
+ '-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable ',
+ 'COMMIT',
+ '# Completed on Mon Dec 6 11:54:13 2010'
+ ]
+
+ def test_static_filters(self):
+ self.fw.execute = self._p
+ instance_ref = db.instance_create(self.context,
+ {'user_id': 'fake',
+ 'project_id': 'fake'})
+ ip = '10.11.12.13'
+
+ network_ref = db.project_get_network(self.context,
+ 'fake')
+
+ fixed_ip = {'address': ip,
+ 'network_id': network_ref['id']}
+
+ admin_ctxt = context.get_admin_context()
+ db.fixed_ip_create(admin_ctxt, fixed_ip)
+ db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
+ 'instance_id': instance_ref['id']})
+
+ secgroup = db.security_group_create(admin_ctxt,
+ {'user_id': 'fake',
+ 'project_id': 'fake',
+ 'name': 'testgroup',
+ 'description': 'test group'})
+
+ db.security_group_rule_create(admin_ctxt,
+ {'parent_group_id': secgroup['id'],
+ 'protocol': 'tcp',
+ 'from_port': 80,
+ 'to_port': 81,
+ 'cidr': '192.168.10.0/24'})
+ db.instance_add_security_group(admin_ctxt, instance_ref['id'],
+ secgroup['id'])
+ instance_ref = db.instance_get(admin_ctxt, instance_ref['id'])
+
+ self.fw.add_instance(instance_ref)
+
+ out_rules = self.fw.modify_rules(self.in_rules)
+
+ in_rules = filter(lambda l: not l.startswith('#'), self.in_rules)
+ for rule in in_rules:
+ if not 'nova' in rule:
+ self.assertTrue(rule in out_rules,
+ 'Rule went missing: %s' % rule)
+
+ print '\n'.join(out_rules)
+
+
+class NWFilterTestCase(test.TestCase):
def setUp(self):
super(NWFilterTestCase, self).setUp()
@@ -224,7 +315,8 @@ class NWFilterTestCase(test.TestCase):
self.fake_libvirt_connection = Mock()
- self.fw = libvirt_conn.NWFilterFirewall(self.fake_libvirt_connection)
+ self.fw = libvirt_conn.NWFilterFirewall(
+ lambda: self.fake_libvirt_connection)
def tearDown(self):
self.manager.delete_project(self.project)
@@ -337,7 +429,7 @@ class NWFilterTestCase(test.TestCase):
self.security_group.id)
instance = db.instance_get(self.context, inst_id)
- self.fw.setup_base_nwfilters()
- self.fw.setup_nwfilters_for_instance(instance)
+ self.fw.setup_basic_filtering(instance)
+ self.fw.prepare_instance_filter(instance)
_ensure_all_called()
self.teardown_security_group()
diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py
index b13455fb0..b40ca004b 100644
--- a/nova/tests/test_volume.py
+++ b/nova/tests/test_volume.py
@@ -19,23 +19,23 @@
Tests for Volume Code.
"""
-import logging
from nova import context
from nova import exception
from nova import db
from nova import flags
+from nova import log as logging
from nova import test
from nova import utils
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.volume')
class VolumeTestCase(test.TestCase):
"""Test Case for volumes."""
def setUp(self):
- logging.getLogger().setLevel(logging.DEBUG)
super(VolumeTestCase, self).setUp()
self.compute = utils.import_object(FLAGS.compute_manager)
self.flags(connection_type='fake')
@@ -159,7 +159,7 @@ class VolumeTestCase(test.TestCase):
volume_id)
self.assert_(iscsi_target not in targets)
targets.append(iscsi_target)
- logging.debug("Target %s allocated", iscsi_target)
+ LOG.debug(_("Target %s allocated"), iscsi_target)
total_slots = FLAGS.iscsi_num_targets
for _index in xrange(total_slots):
volume_id = self._create_volume()
diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py
index 55f751f11..292bd9ba9 100644
--- a/nova/tests/xenapi/stubs.py
+++ b/nova/tests/xenapi/stubs.py
@@ -41,9 +41,33 @@ def stubout_instance_snapshot(stubs):
rv = done.wait()
return rv
+ def fake_loop(self):
+ pass
+
stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task',
fake_wait_for_task)
+ stubs.Set(xenapi_conn.XenAPISession, '_stop_loop', fake_loop)
+
+ from nova.virt.xenapi.fake import create_vdi
+ name_label = "instance-%s" % instance_id
+ #TODO: create fake SR record
+ sr_ref = "fakesr"
+ vdi_ref = create_vdi(name_label=name_label, read_only=False,
+ sr_ref=sr_ref, sharable=False)
+ vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
+ vdi_uuid = vdi_rec['uuid']
+ return vdi_uuid
+
+ stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
+
+ def fake_parse_xmlrpc_value(val):
+ return val
+
+ stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value)
+
+ def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
+ original_parent_uuid):
from nova.virt.xenapi.fake import create_vdi
name_label = "instance-%s" % instance_id
#TODO: create fake SR record
diff --git a/nova/twistd.py b/nova/twistd.py
index 29be9c4e1..556271999 100644
--- a/nova/twistd.py
+++ b/nova/twistd.py
@@ -22,7 +22,6 @@ manage pid files and support syslogging.
"""
import gflags
-import logging
import os
import signal
import sys
@@ -34,6 +33,7 @@ from twisted.python import runtime
from twisted.python import usage
from nova import flags
+from nova import log as logging
if runtime.platformType == "win32":
@@ -234,22 +234,12 @@ def serve(filename):
OptionsClass = WrapTwistedOptions(TwistdServerOptions)
options = OptionsClass()
argv = options.parseOptions()
- logging.getLogger('amqplib').setLevel(logging.WARN)
FLAGS.python = filename
FLAGS.no_save = True
if not FLAGS.pidfile:
FLAGS.pidfile = '%s.pid' % name
elif FLAGS.pidfile.endswith('twistd.pid'):
FLAGS.pidfile = FLAGS.pidfile.replace('twistd.pid', '%s.pid' % name)
- # NOTE(vish): if we're running nodaemon, redirect the log to stdout
- if FLAGS.nodaemon and not FLAGS.logfile:
- FLAGS.logfile = "-"
- if not FLAGS.logfile:
- FLAGS.logfile = '%s.log' % name
- elif FLAGS.logfile.endswith('twistd.log'):
- FLAGS.logfile = FLAGS.logfile.replace('twistd.log', '%s.log' % name)
- if FLAGS.logdir:
- FLAGS.logfile = os.path.join(FLAGS.logdir, FLAGS.logfile)
if not FLAGS.prefix:
FLAGS.prefix = name
elif FLAGS.prefix.endswith('twisted'):
@@ -270,19 +260,10 @@ def serve(filename):
print 'usage: %s [options] [start|stop|restart]' % argv[0]
sys.exit(1)
- formatter = logging.Formatter(
- '(%(name)s): %(levelname)s %(message)s')
- handler = logging.StreamHandler(log.StdioOnnaStick())
- handler.setFormatter(formatter)
- logging.getLogger().addHandler(handler)
-
- if FLAGS.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
- else:
- logging.getLogger().setLevel(logging.WARNING)
-
+ logging.basicConfig()
logging.debug(_("Full set of FLAGS:"))
for flag in FLAGS:
logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
+ logging.audit(_("Starting %s"), name)
twistd.runApp(options)
diff --git a/nova/utils.py b/nova/utils.py
index 15112faa2..aadbec532 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -22,7 +22,6 @@ System-level utilities and helper functions.
import datetime
import inspect
-import logging
import os
import random
import subprocess
@@ -37,8 +36,10 @@ from eventlet import greenthread
from nova import exception
from nova.exception import ProcessExecutionError
+from nova import log as logging
+LOG = logging.getLogger("nova.utils")
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
@@ -109,7 +110,7 @@ def vpn_ping(address, port, timeout=0.05, session_id=None):
def fetchfile(url, target):
- logging.debug(_("Fetching %s") % url)
+ LOG.debug(_("Fetching %s") % url)
# c = pycurl.Curl()
# fp = open(target, "wb")
# c.setopt(c.URL, url)
@@ -121,7 +122,7 @@ def fetchfile(url, target):
def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
- logging.debug(_("Running cmd (subprocess): %s"), cmd)
+ LOG.debug(_("Running cmd (subprocess): %s"), cmd)
env = os.environ.copy()
if addl_env:
env.update(addl_env)
@@ -134,7 +135,7 @@ def execute(cmd, process_input=None, addl_env=None, check_exit_code=True):
result = obj.communicate()
obj.stdin.close()
if obj.returncode:
- logging.debug(_("Result was %s") % (obj.returncode))
+ LOG.debug(_("Result was %s") % (obj.returncode))
if check_exit_code and obj.returncode != 0:
(stdout, stderr) = result
raise ProcessExecutionError(exit_code=obj.returncode,
@@ -167,12 +168,12 @@ def default_flagfile(filename='nova.conf'):
def debug(arg):
- logging.debug('debug in callback: %s', arg)
+ LOG.debug(_('debug in callback: %s'), arg)
return arg
def runthis(prompt, cmd, check_exit_code=True):
- logging.debug(_("Running %s") % (cmd))
+ LOG.debug(_("Running %s"), (cmd))
rv, err = execute(cmd, check_exit_code=check_exit_code)
@@ -194,19 +195,6 @@ def last_octet(address):
return int(address.split(".")[-1])
-def get_my_ip():
- """Returns the actual ip of the local machine."""
- try:
- csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- csock.connect(('8.8.8.8', 80))
- (addr, port) = csock.getsockname()
- csock.close()
- return addr
- except socket.gaierror as ex:
- logging.warn(_("Couldn't get IP, using 127.0.0.1 %s"), ex)
- return "127.0.0.1"
-
-
def utcnow():
"""Overridable version of datetime.datetime.utcnow."""
if utcnow.override_time:
@@ -296,7 +284,7 @@ class LazyPluggable(object):
fromlist = backend
self.__backend = __import__(name, None, None, fromlist)
- logging.info('backend %s', self.__backend)
+ LOG.debug(_('backend %s'), self.__backend)
return self.__backend
def __getattr__(self, key):
diff --git a/nova/version.py b/nova/version.py
new file mode 100644
index 000000000..7b27acb6a
--- /dev/null
+++ b/nova/version.py
@@ -0,0 +1,46 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 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.
+
+try:
+ from nova.vcsversion import version_info
+except ImportError:
+ version_info = {'branch_nick': u'LOCALBRANCH',
+ 'revision_id': 'LOCALREVISION',
+ 'revno': 0}
+
+NOVA_VERSION = ['2011', '1']
+YEAR, COUNT = NOVA_VERSION
+
+FINAL = False # This becomes true at Release Candidate time
+
+
+def canonical_version_string():
+ return '.'.join([YEAR, COUNT])
+
+
+def version_string():
+ if FINAL:
+ return canonical_version_string()
+ else:
+ return '%s-dev' % (canonical_version_string(),)
+
+
+def vcs_version_string():
+ return "%s:%s" % (version_info['branch_nick'], version_info['revision_id'])
+
+
+def version_string_with_vcs():
+ return "%s-%s" % (canonical_version_string(), vcs_version_string())
diff --git a/nova/virt/connection.py b/nova/virt/connection.py
index 846423afe..13181b730 100644
--- a/nova/virt/connection.py
+++ b/nova/virt/connection.py
@@ -19,16 +19,17 @@
"""Abstraction of the underlying virtualization API."""
-import logging
import sys
from nova import flags
+from nova import log as logging
from nova.virt import fake
from nova.virt import libvirt_conn
from nova.virt import xenapi_conn
from nova.virt import hyperv
+LOG = logging.getLogger("nova.virt.connection")
FLAGS = flags.FLAGS
@@ -69,6 +70,6 @@ def get_connection(read_only=False):
raise Exception('Unknown connection type "%s"' % t)
if conn is None:
- logging.error(_('Failed to open connection to the hypervisor'))
+ LOG.error(_('Failed to open connection to the hypervisor'))
sys.exit(1)
return conn
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 4b9f6f946..d71387ac0 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -61,11 +61,11 @@ Using the Python WMI library:
"""
import os
-import logging
import time
from nova import exception
from nova import flags
+from nova import log as logging
from nova.auth import manager
from nova.compute import power_state
from nova.virt import images
@@ -76,6 +76,9 @@ wmi = None
FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.virt.hyperv')
+
+
HYPERV_POWER_STATE = {
3: power_state.SHUTDOWN,
2: power_state.RUNNING,
@@ -112,7 +115,7 @@ class HyperVConnection(object):
def init_host(self):
#FIXME(chiradeep): implement this
- logging.debug(_('In init host'))
+ LOG.debug(_('In init host'))
pass
def list_instances(self):
@@ -142,11 +145,11 @@ class HyperVConnection(object):
self._create_disk(instance['name'], vhdfile)
self._create_nic(instance['name'], instance['mac_address'])
- logging.debug(_('Starting VM %s '), instance.name)
+ LOG.debug(_('Starting VM %s '), instance.name)
self._set_vm_state(instance['name'], 'Enabled')
- logging.info(_('Started VM %s '), instance.name)
+ LOG.info(_('Started VM %s '), instance.name)
except Exception as exn:
- logging.error(_('spawn vm failed: %s'), exn)
+ LOG.exception(_('spawn vm failed: %s'), exn)
self.destroy(instance)
def _create_vm(self, instance):
@@ -165,7 +168,7 @@ class HyperVConnection(object):
if not success:
raise Exception(_('Failed to create VM %s'), instance.name)
- logging.debug(_('Created VM %s...'), instance.name)
+ LOG.debug(_('Created VM %s...'), instance.name)
vm = self._conn.Msvm_ComputerSystem(ElementName=instance.name)[0]
vmsettings = vm.associators(
@@ -182,7 +185,7 @@ class HyperVConnection(object):
(job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
vm.path_(), [memsetting.GetText_(1)])
- logging.debug(_('Set memory for vm %s...'), instance.name)
+ LOG.debug(_('Set memory for vm %s...'), instance.name)
procsetting = vmsetting.associators(
wmi_result_class='Msvm_ProcessorSettingData')[0]
vcpus = long(instance['vcpus'])
@@ -192,12 +195,12 @@ class HyperVConnection(object):
(job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
vm.path_(), [procsetting.GetText_(1)])
- logging.debug(_('Set vcpus for vm %s...'), instance.name)
+ LOG.debug(_('Set vcpus for vm %s...'), instance.name)
def _create_disk(self, vm_name, vhdfile):
"""Create a disk and attach it to the vm"""
- logging.debug(_('Creating disk for %s by attaching disk file %s'),
- vm_name, vhdfile)
+ LOG.debug(_('Creating disk for %s by attaching disk file %s'),
+ vm_name, vhdfile)
#Find the IDE controller for the vm.
vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
vm = vms[0]
@@ -224,7 +227,7 @@ class HyperVConnection(object):
raise Exception(_('Failed to add diskdrive to VM %s'),
vm_name)
diskdrive_path = new_resources[0]
- logging.debug(_('New disk drive path is %s'), diskdrive_path)
+ LOG.debug(_('New disk drive path is %s'), diskdrive_path)
#Find the default VHD disk object.
vhddefault = self._conn.query(
"SELECT * FROM Msvm_ResourceAllocationSettingData \
@@ -243,11 +246,11 @@ class HyperVConnection(object):
if new_resources is None:
raise Exception(_('Failed to add vhd file to VM %s'),
vm_name)
- logging.info(_('Created disk for %s'), vm_name)
+ LOG.info(_('Created disk for %s'), vm_name)
def _create_nic(self, vm_name, mac):
"""Create a (emulated) nic and attach it to the vm"""
- logging.debug(_('Creating nic for %s '), vm_name)
+ LOG.debug(_('Creating nic for %s '), vm_name)
#Find the vswitch that is connected to the physical nic.
vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
extswitch = self._find_external_network()
@@ -266,11 +269,11 @@ class HyperVConnection(object):
(new_port, ret_val) = switch_svc.CreateSwitchPort(vm_name, vm_name,
"", extswitch.path_())
if ret_val != 0:
- logging.error(_('Failed creating a port on the external vswitch'))
+ LOG.error(_('Failed creating a port on the external vswitch'))
raise Exception(_('Failed creating port for %s'),
vm_name)
- logging.debug(_("Created switch port %s on switch %s"),
- vm_name, extswitch.path_())
+ LOG.debug(_("Created switch port %s on switch %s"),
+ vm_name, extswitch.path_())
#Connect the new nic to the new port.
new_nic_data.Connection = [new_port]
new_nic_data.ElementName = vm_name + ' nic'
@@ -281,7 +284,7 @@ class HyperVConnection(object):
if new_resources is None:
raise Exception(_('Failed to add nic to VM %s'),
vm_name)
- logging.info(_("Created nic for %s "), vm_name)
+ LOG.info(_("Created nic for %s "), vm_name)
def _add_virt_resource(self, res_setting_data, target_vm):
"""Add a new resource (disk/nic) to the VM"""
@@ -314,10 +317,10 @@ class HyperVConnection(object):
time.sleep(0.1)
job = self._conn.Msvm_ConcreteJob(InstanceID=inst_id)[0]
if job.JobState != WMI_JOB_STATE_COMPLETED:
- logging.debug(_("WMI job failed: %s"), job.ErrorSummaryDescription)
+ LOG.debug(_("WMI job failed: %s"), job.ErrorSummaryDescription)
return False
- logging.debug(_("WMI job succeeded: %s, Elapsed=%s "), job.Description,
- job.ElapsedTime)
+ LOG.debug(_("WMI job succeeded: %s, Elapsed=%s "), job.Description,
+ job.ElapsedTime)
return True
def _find_external_network(self):
@@ -352,7 +355,7 @@ class HyperVConnection(object):
def destroy(self, instance):
"""Destroy the VM. Also destroy the associated VHD disk files"""
- logging.debug(_("Got request to destroy vm %s"), instance.name)
+ LOG.debug(_("Got request to destroy vm %s"), instance.name)
vm = self._lookup(instance.name)
if vm is None:
return
@@ -383,7 +386,7 @@ class HyperVConnection(object):
vhdfile = self._cim_conn.CIM_DataFile(Name=disk)
for vf in vhdfile:
vf.Delete()
- logging.debug(_("Del: disk %s vm %s"), vhdfile, instance.name)
+ LOG.debug(_("Del: disk %s vm %s"), vhdfile, instance.name)
def get_info(self, instance_id):
"""Get information about the VM"""
@@ -399,12 +402,12 @@ class HyperVConnection(object):
summary_info = vs_man_svc.GetSummaryInformation(
[4, 100, 103, 105], settings_paths)[1]
info = summary_info[0]
- logging.debug(_("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \
+ LOG.debug(_("Got Info for vm %s: state=%s, mem=%s, num_cpu=%s, \
cpu_time=%s"), instance_id,
- str(HYPERV_POWER_STATE[info.EnabledState]),
- str(info.MemoryUsage),
- str(info.NumberOfProcessors),
- str(info.UpTime))
+ str(HYPERV_POWER_STATE[info.EnabledState]),
+ str(info.MemoryUsage),
+ str(info.NumberOfProcessors),
+ str(info.UpTime))
return {'state': HYPERV_POWER_STATE[info.EnabledState],
'max_mem': info.MemoryUsage,
@@ -438,11 +441,11 @@ class HyperVConnection(object):
#already in the state requested
success = True
if success:
- logging.info(_("Successfully changed vm state of %s to %s"),
- vm_name, req_state)
+ LOG.info(_("Successfully changed vm state of %s to %s"), vm_name,
+ req_state)
else:
- logging.error(_("Failed to change vm state of %s to %s"),
- vm_name, req_state)
+ LOG.error(_("Failed to change vm state of %s to %s"), vm_name,
+ req_state)
raise Exception(_("Failed to change vm state of %s to %s"),
vm_name, req_state)
diff --git a/nova/virt/images.py b/nova/virt/images.py
index 2d03da4b4..ecf0e5efb 100644
--- a/nova/virt/images.py
+++ b/nova/virt/images.py
@@ -21,7 +21,6 @@
Handling of VM disk images.
"""
-import logging
import os.path
import shutil
import sys
@@ -30,6 +29,7 @@ import urllib2
import urlparse
from nova import flags
+from nova import log as logging
from nova import utils
from nova.auth import manager
from nova.auth import signer
@@ -40,6 +40,8 @@ FLAGS = flags.FLAGS
flags.DEFINE_bool('use_s3', True,
'whether to get images from s3 or use local copy')
+LOG = logging.getLogger('nova.virt.images')
+
def fetch(image, path, user, project):
if FLAGS.use_s3:
@@ -65,7 +67,7 @@ def _fetch_image_no_curl(url, path, headers):
urlopened = urllib2.urlopen(request)
urlretrieve(urlopened, path)
- logging.debug(_("Finished retreving %s -- placed in %s"), url, path)
+ LOG.debug(_("Finished retreving %s -- placed in %s"), url, path)
def _fetch_s3_image(image, path, user, project):
@@ -89,7 +91,7 @@ def _fetch_s3_image(image, path, user, project):
else:
cmd = ['/usr/bin/curl', '--fail', '--silent', url]
for (k, v) in headers.iteritems():
- cmd += ['-H', '%s: %s' % (k, v)]
+ cmd += ['-H', '\'%s: %s\'' % (k, v)]
cmd += ['-o', path]
cmd_out = ' '.join(cmd)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 00edfbdc8..3a4b6d469 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -36,7 +36,6 @@ Supports KVM, QEMU, UML, and XEN.
"""
-import logging
import os
import shutil
@@ -50,6 +49,7 @@ 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 utils
#from nova.api import context
from nova.auth import manager
@@ -62,6 +62,7 @@ libvirt = None
libxml2 = None
Template = None
+LOG = logging.getLogger('nova.virt.libvirt_conn')
FLAGS = flags.FLAGS
# TODO(vish): These flags should probably go into a shared location
@@ -85,6 +86,9 @@ flags.DEFINE_string('libvirt_uri',
flags.DEFINE_bool('allow_project_net_traffic',
True,
'Whether to allow in project network traffic')
+flags.DEFINE_string('firewall_driver',
+ 'nova.virt.libvirt_conn.IptablesFirewallDriver',
+ 'Firewall driver (defaults to iptables)')
def get_connection(read_only):
@@ -124,16 +128,24 @@ class LibvirtConnection(object):
self._wrapped_conn = None
self.read_only = read_only
+ self.nwfilter = NWFilterFirewall(self._get_connection)
+
+ if not FLAGS.firewall_driver:
+ self.firewall_driver = self.nwfilter
+ self.nwfilter.handle_security_groups = True
+ else:
+ self.firewall_driver = utils.import_object(FLAGS.firewall_driver)
+
def init_host(self):
- NWFilterFirewall(self._conn).setup_base_nwfilters()
+ pass
- @property
- def _conn(self):
+ def _get_connection(self):
if not self._wrapped_conn or not self._test_connection():
- logging.debug(_('Connecting to libvirt: %s') % self.libvirt_uri)
+ LOG.debug(_('Connecting to libvirt: %s'), self.libvirt_uri)
self._wrapped_conn = self._connect(self.libvirt_uri,
self.read_only)
return self._wrapped_conn
+ _conn = property(_get_connection)
def _test_connection(self):
try:
@@ -142,7 +154,7 @@ class LibvirtConnection(object):
except libvirt.libvirtError as e:
if e.get_error_code() == libvirt.VIR_ERR_SYSTEM_ERROR and \
e.get_error_domain() == libvirt.VIR_FROM_REMOTE:
- logging.debug(_('Connection to libvirt broke'))
+ LOG.debug(_('Connection to libvirt broke'))
return False
raise
@@ -214,8 +226,8 @@ class LibvirtConnection(object):
def _cleanup(self, instance):
target = os.path.join(FLAGS.instances_path, instance['name'])
- logging.info(_('instance %s: deleting instance files %s'),
- instance['name'], target)
+ LOG.info(_('instance %s: deleting instance files %s'),
+ instance['name'], target)
if os.path.exists(target):
shutil.rmtree(target)
@@ -279,10 +291,10 @@ class LibvirtConnection(object):
db.instance_set_state(context.get_admin_context(),
instance['id'], state)
if state == power_state.RUNNING:
- logging.debug(_('instance %s: rebooted'), instance['name'])
+ LOG.debug(_('instance %s: rebooted'), instance['name'])
timer.stop()
except Exception, exn:
- logging.error(_('_wait_for_reboot failed: %s'), exn)
+ LOG.exception(_('_wait_for_reboot failed: %s'), exn)
db.instance_set_state(context.get_admin_context(),
instance['id'],
power_state.SHUTDOWN)
@@ -325,10 +337,10 @@ class LibvirtConnection(object):
state = self.get_info(instance['name'])['state']
db.instance_set_state(None, instance['id'], state)
if state == power_state.RUNNING:
- logging.debug(_('instance %s: rescued'), instance['name'])
+ LOG.debug(_('instance %s: rescued'), instance['name'])
timer.stop()
except Exception, exn:
- logging.error(_('_wait_for_rescue failed: %s'), exn)
+ LOG.exception(_('_wait_for_rescue failed: %s'), exn)
db.instance_set_state(None,
instance['id'],
power_state.SHUTDOWN)
@@ -350,10 +362,13 @@ class LibvirtConnection(object):
instance['id'],
power_state.NOSTATE,
'launching')
- NWFilterFirewall(self._conn).setup_nwfilters_for_instance(instance)
+
+ self.nwfilter.setup_basic_filtering(instance)
+ self.firewall_driver.prepare_instance_filter(instance)
self._create_image(instance, xml)
self._conn.createXML(xml, 0)
- logging.debug(_("instance %s: is running"), instance['name'])
+ LOG.debug(_("instance %s: is running"), instance['name'])
+ self.firewall_driver.apply_instance_filter(instance)
timer = utils.LoopingCall(f=None)
@@ -363,11 +378,11 @@ class LibvirtConnection(object):
db.instance_set_state(context.get_admin_context(),
instance['id'], state)
if state == power_state.RUNNING:
- logging.debug(_('instance %s: booted'), instance['name'])
+ LOG.debug(_('instance %s: booted'), instance['name'])
timer.stop()
except:
- logging.exception(_('instance %s: failed to boot'),
- instance['name'])
+ LOG.exception(_('instance %s: failed to boot'),
+ instance['name'])
db.instance_set_state(context.get_admin_context(),
instance['id'],
power_state.SHUTDOWN)
@@ -377,11 +392,11 @@ class LibvirtConnection(object):
return timer.start(interval=0.5, now=True)
def _flush_xen_console(self, virsh_output):
- logging.info('virsh said: %r' % (virsh_output,))
+ LOG.info(_('virsh said: %r'), virsh_output)
virsh_output = virsh_output[0].strip()
if virsh_output.startswith('/dev/'):
- logging.info(_('cool, it\'s a device'))
+ LOG.info(_('cool, it\'s a device'))
out, err = utils.execute("sudo dd if=%s iflag=nonblock" %
virsh_output, check_exit_code=False)
return out
@@ -389,7 +404,7 @@ class LibvirtConnection(object):
return ''
def _append_to_file(self, data, fpath):
- logging.info(_('data: %r, fpath: %r') % (data, fpath))
+ LOG.info(_('data: %r, fpath: %r'), data, fpath)
fp = open(fpath, 'a+')
fp.write(data)
return fpath
@@ -397,7 +412,7 @@ class LibvirtConnection(object):
def _dump_file(self, fpath):
fp = open(fpath, 'r+')
contents = fp.read()
- logging.info('Contents: %r' % (contents,))
+ LOG.info(_('Contents of file %s: %r'), fpath, contents)
return contents
@exception.wrap_exception
@@ -431,7 +446,7 @@ class LibvirtConnection(object):
# TODO(termie): these are blocking calls, it would be great
# if they weren't.
- logging.info(_('instance %s: Creating image'), inst['name'])
+ LOG.info(_('instance %s: Creating image'), inst['name'])
f = open(basepath('libvirt.xml'), 'w')
f.write(libvirt_xml)
f.close()
@@ -487,10 +502,10 @@ class LibvirtConnection(object):
'dns': network_ref['dns']}
if key or net:
if key:
- logging.info(_('instance %s: injecting key into image %s'),
+ LOG.info(_('instance %s: injecting key into image %s'),
inst['name'], inst.image_id)
if net:
- logging.info(_('instance %s: injecting net into image %s'),
+ LOG.info(_('instance %s: injecting net into image %s'),
inst['name'], inst.image_id)
try:
disk.inject_data(basepath('disk-raw'), key, net,
@@ -498,9 +513,9 @@ class LibvirtConnection(object):
execute=execute)
except Exception as e:
# This could be a windows image, or a vmdk format disk
- logging.warn(_('instance %s: ignoring error injecting data'
- ' into image %s (%s)'),
- inst['name'], inst.image_id, e)
+ LOG.warn(_('instance %s: ignoring error injecting data'
+ ' into image %s (%s)'),
+ inst['name'], inst.image_id, e)
if inst['kernel_id']:
if os.path.exists(basepath('disk')):
@@ -526,8 +541,10 @@ class LibvirtConnection(object):
def to_xml(self, instance, rescue=False):
# TODO(termie): cache?
- logging.debug(_('instance %s: starting toXML method'),
- instance['name'])
+ LOG.debug(_('instance %s: starting toXML method'), instance['name'])
+ network = db.project_get_network(context.get_admin_context(),
+ instance['project_id'])
+ LOG.debug(_('instance %s: starting toXML method'), instance['name'])
network = db.network_get_by_instance(context.get_admin_context(),
instance['id'])
# FIXME(vish): stick this in db
@@ -569,7 +586,7 @@ class LibvirtConnection(object):
xml_info['disk'] = xml_info['basepath'] + "/disk"
xml = str(Template(self.libvirt_xml, searchList=[xml_info]))
- logging.debug(_('instance %s: finished toXML method'),
+ LOG.debug(_('instance %s: finished toXML method'),
instance['name'])
return xml
@@ -690,18 +707,55 @@ class LibvirtConnection(object):
domain = self._conn.lookupByName(instance_name)
return domain.interfaceStats(interface)
- def refresh_security_group(self, security_group_id):
- fw = NWFilterFirewall(self._conn)
- fw.ensure_security_group_filter(security_group_id)
+ def refresh_security_group_rules(self, security_group_id):
+ self.firewall_driver.refresh_security_group_rules(security_group_id)
+
+ def refresh_security_group_members(self, security_group_id):
+ self.firewall_driver.refresh_security_group_members(security_group_id)
+
+
+class FirewallDriver(object):
+ def prepare_instance_filter(self, instance):
+ """Prepare filters for the instance.
+
+ At this point, the instance isn't running yet."""
+ raise NotImplementedError()
+
+ def apply_instance_filter(self, instance):
+ """Apply instance filter.
+
+ Once this method returns, the instance should be firewalled
+ appropriately. This method should as far as possible be a
+ no-op. It's vastly preferred to get everything set up in
+ prepare_instance_filter.
+ """
+ raise NotImplementedError()
+
+ def refresh_security_group_rules(self, security_group_id):
+ """Refresh security group rules from data store
+
+ Gets called when a rule has been added to or removed from
+ the security group."""
+ raise NotImplementedError()
+
+ def refresh_security_group_members(self, security_group_id):
+ """Refresh security group members from data store
+ Gets called when an instance gets added to or removed from
+ the security group."""
+ raise NotImplementedError()
-class NWFilterFirewall(object):
+
+class NWFilterFirewall(FirewallDriver):
"""
This class implements a network filtering mechanism versatile
enough for EC2 style Security Group filtering by leveraging
libvirt's nwfilter.
First, all instances get a filter ("nova-base-filter") applied.
+ This filter provides some basic security such as protection against
+ MAC spoofing, IP spoofing, and ARP spoofing.
+
This filter drops all incoming ipv4 and ipv6 connections.
Outgoing connections are never blocked.
@@ -735,38 +789,79 @@ class NWFilterFirewall(object):
(*) This sentence brought to you by the redundancy department of
redundancy.
+
"""
def __init__(self, get_connection):
- self._conn = get_connection
-
- nova_base_filter = '''<filter name='nova-base' chain='root'>
- <uuid>26717364-50cf-42d1-8185-29bf893ab110</uuid>
- <filterref filter='no-mac-spoofing'/>
- <filterref filter='no-ip-spoofing'/>
- <filterref filter='no-arp-spoofing'/>
- <filterref filter='allow-dhcp-server'/>
- <filterref filter='nova-allow-dhcp-server'/>
- <filterref filter='nova-base-ipv4'/>
- <filterref filter='nova-base-ipv6'/>
- </filter>'''
-
- nova_dhcp_filter = '''<filter name='nova-allow-dhcp-server' chain='ipv4'>
- <uuid>891e4787-e5c0-d59b-cbd6-41bc3c6b36fc</uuid>
- <rule action='accept' direction='out'
- priority='100'>
- <udp srcipaddr='0.0.0.0'
- dstipaddr='255.255.255.255'
- srcportstart='68'
- dstportstart='67'/>
- </rule>
- <rule action='accept' direction='in'
- priority='100'>
- <udp srcipaddr='$DHCPSERVER'
- srcportstart='67'
- dstportstart='68'/>
- </rule>
- </filter>'''
+ self._libvirt_get_connection = get_connection
+ self.static_filters_configured = False
+ self.handle_security_groups = False
+
+ def _get_connection(self):
+ return self._libvirt_get_connection()
+ _conn = property(_get_connection)
+
+ def nova_dhcp_filter(self):
+ """The standard allow-dhcp-server filter is an <ip> one, so it uses
+ ebtables to allow traffic through. Without a corresponding rule in
+ iptables, it'll get blocked anyway."""
+
+ return '''<filter name='nova-allow-dhcp-server' chain='ipv4'>
+ <uuid>891e4787-e5c0-d59b-cbd6-41bc3c6b36fc</uuid>
+ <rule action='accept' direction='out'
+ priority='100'>
+ <udp srcipaddr='0.0.0.0'
+ dstipaddr='255.255.255.255'
+ srcportstart='68'
+ dstportstart='67'/>
+ </rule>
+ <rule action='accept' direction='in'
+ priority='100'>
+ <udp srcipaddr='$DHCPSERVER'
+ srcportstart='67'
+ dstportstart='68'/>
+ </rule>
+ </filter>'''
+
+ def setup_basic_filtering(self, instance):
+ """Set up basic filtering (MAC, IP, and ARP spoofing protection)"""
+ logging.info('called setup_basic_filtering in nwfilter')
+
+ if self.handle_security_groups:
+ # No point in setting up a filter set that we'll be overriding
+ # anyway.
+ return
+
+ logging.info('ensuring static filters')
+ self._ensure_static_filters()
+
+ instance_filter_name = self._instance_filter_name(instance)
+ self._define_filter(self._filter_container(instance_filter_name,
+ ['nova-base']))
+
+ def _ensure_static_filters(self):
+ if self.static_filters_configured:
+ return
+
+ self._define_filter(self._filter_container('nova-base',
+ ['no-mac-spoofing',
+ 'no-ip-spoofing',
+ 'no-arp-spoofing',
+ 'allow-dhcp-server']))
+ self._define_filter(self.nova_base_ipv4_filter)
+ self._define_filter(self.nova_base_ipv6_filter)
+ self._define_filter(self.nova_dhcp_filter)
+ self._define_filter(self.nova_vpn_filter)
+ if FLAGS.allow_project_net_traffic:
+ self._define_filter(self.nova_project_filter)
+
+ self.static_filters_configured = True
+
+ def _filter_container(self, name, filters):
+ xml = '''<filter name='%s' chain='root'>%s</filter>''' % (
+ name,
+ ''.join(["<filterref filter='%s'/>" % (f,) for f in filters]))
+ return xml
nova_vpn_filter = '''<filter name='nova-vpn' chain='root'>
<uuid>2086015e-cf03-11df-8c5d-080027c27973</uuid>
@@ -780,7 +875,7 @@ class NWFilterFirewall(object):
retval = "<filter name='nova-base-ipv4' chain='ipv4'>"
for protocol in ['tcp', 'udp', 'icmp']:
for direction, action, priority in [('out', 'accept', 399),
- ('inout', 'drop', 400)]:
+ ('in', 'drop', 400)]:
retval += """<rule action='%s' direction='%s' priority='%d'>
<%s />
</rule>""" % (action, direction,
@@ -792,7 +887,7 @@ class NWFilterFirewall(object):
retval = "<filter name='nova-base-ipv6' chain='ipv6'>"
for protocol in ['tcp', 'udp', 'icmp']:
for direction, action, priority in [('out', 'accept', 399),
- ('inout', 'drop', 400)]:
+ ('in', 'drop', 400)]:
retval += """<rule action='%s' direction='%s' priority='%d'>
<%s-ipv6 />
</rule>""" % (action, direction,
@@ -816,43 +911,49 @@ class NWFilterFirewall(object):
# execute in a native thread and block current greenthread until done
tpool.execute(self._conn.nwfilterDefineXML, xml)
- def setup_base_nwfilters(self):
- self._define_filter(self.nova_base_ipv4_filter)
- self._define_filter(self.nova_base_ipv6_filter)
- self._define_filter(self.nova_dhcp_filter)
- self._define_filter(self.nova_base_filter)
- self._define_filter(self.nova_vpn_filter)
- if FLAGS.allow_project_net_traffic:
- self._define_filter(self.nova_project_filter)
-
- def setup_nwfilters_for_instance(self, instance):
+ def prepare_instance_filter(self, instance):
"""
Creates an NWFilter for the given instance. In the process,
it makes sure the filters for the security groups as well as
the base filter are all in place.
"""
- nwfilter_xml = ("<filter name='nova-instance-%s' "
- "chain='root'>\n") % instance['name']
-
if instance['image_id'] == FLAGS.vpn_image_id:
- nwfilter_xml += " <filterref filter='nova-vpn' />\n"
+ base_filter = 'nova-vpn'
else:
- nwfilter_xml += " <filterref filter='nova-base' />\n"
+ base_filter = 'nova-base'
+
+ instance_filter_name = self._instance_filter_name(instance)
+ instance_secgroup_filter_name = '%s-secgroup' % (instance_filter_name,)
+ instance_filter_children = [base_filter, instance_secgroup_filter_name]
+ instance_secgroup_filter_children = ['nova-base-ipv4',
+ 'nova-base-ipv6',
+ 'nova-allow-dhcp-server']
+
+ ctxt = context.get_admin_context()
if FLAGS.allow_project_net_traffic:
- nwfilter_xml += " <filterref filter='nova-project' />\n"
+ instance_filter_children += ['nova-project']
+
+ for security_group in db.security_group_get_by_instance(ctxt,
+ instance['id']):
+
+ self.refresh_security_group_rules(security_group['id'])
+
+ instance_secgroup_filter_children += [('nova-secgroup-%s' %
+ security_group['id'])]
- for security_group in instance.security_groups:
- self.ensure_security_group_filter(security_group['id'])
+ self._define_filter(
+ self._filter_container(instance_secgroup_filter_name,
+ instance_secgroup_filter_children))
- nwfilter_xml += (" <filterref filter='nova-secgroup-%d' "
- "/>\n") % security_group['id']
- nwfilter_xml += "</filter>"
+ self._define_filter(
+ self._filter_container(instance_filter_name,
+ instance_filter_children))
- self._define_filter(nwfilter_xml)
+ return
- def ensure_security_group_filter(self, security_group_id):
+ def refresh_security_group_rules(self, security_group_id):
return self._define_filter(
self.security_group_to_nwfilter_xml(security_group_id))
@@ -870,9 +971,9 @@ class NWFilterFirewall(object):
rule_xml += "dstportstart='%s' dstportend='%s' " % \
(rule.from_port, rule.to_port)
elif rule.protocol == 'icmp':
- logging.info('rule.protocol: %r, rule.from_port: %r, '
- 'rule.to_port: %r' %
- (rule.protocol, rule.from_port, rule.to_port))
+ LOG.info('rule.protocol: %r, rule.from_port: %r, '
+ 'rule.to_port: %r', rule.protocol,
+ rule.from_port, rule.to_port)
if rule.from_port != -1:
rule_xml += "type='%s' " % rule.from_port
if rule.to_port != -1:
@@ -883,3 +984,162 @@ class NWFilterFirewall(object):
xml = "<filter name='nova-secgroup-%s' chain='ipv4'>%s</filter>" % \
(security_group_id, rule_xml,)
return xml
+
+ def _instance_filter_name(self, instance):
+ return 'nova-instance-%s' % instance['name']
+
+
+class IptablesFirewallDriver(FirewallDriver):
+ def __init__(self, execute=None):
+ self.execute = execute or utils.execute
+ self.instances = set()
+
+ def apply_instance_filter(self, instance):
+ """No-op. Everything is done in prepare_instance_filter"""
+ pass
+
+ def remove_instance(self, instance):
+ self.instances.remove(instance)
+
+ def add_instance(self, instance):
+ self.instances.add(instance)
+
+ def prepare_instance_filter(self, instance):
+ self.add_instance(instance)
+ self.apply_ruleset()
+
+ def apply_ruleset(self):
+ current_filter, _ = self.execute('sudo iptables-save -t filter')
+ current_lines = current_filter.split('\n')
+ new_filter = self.modify_rules(current_lines)
+ self.execute('sudo iptables-restore',
+ process_input='\n'.join(new_filter))
+
+ def modify_rules(self, current_lines):
+ ctxt = context.get_admin_context()
+ # Remove any trace of nova rules.
+ new_filter = filter(lambda l: 'nova-' not in l, current_lines)
+
+ seen_chains = False
+ for rules_index in range(len(new_filter)):
+ if not seen_chains:
+ if new_filter[rules_index].startswith(':'):
+ seen_chains = True
+ elif seen_chains == 1:
+ if not new_filter[rules_index].startswith(':'):
+ break
+
+ our_chains = [':nova-ipv4-fallback - [0:0]']
+ our_rules = ['-A nova-ipv4-fallback -j DROP']
+
+ our_chains += [':nova-local - [0:0]']
+ our_rules += ['-A FORWARD -j nova-local']
+
+ security_groups = set()
+ # Add our chains
+ # First, we add instance chains and rules
+ for instance in self.instances:
+ chain_name = self._instance_chain_name(instance)
+ ip_address = self._ip_for_instance(instance)
+
+ our_chains += [':%s - [0:0]' % chain_name]
+
+ # Jump to the per-instance chain
+ our_rules += ['-A nova-local -d %s -j %s' % (ip_address,
+ chain_name)]
+
+ # Always drop invalid packets
+ our_rules += ['-A %s -m state --state '
+ 'INVALID -j DROP' % (chain_name,)]
+
+ # Allow established connections
+ our_rules += ['-A %s -m state --state '
+ 'ESTABLISHED,RELATED -j ACCEPT' % (chain_name,)]
+
+ # Jump to each security group chain in turn
+ for security_group in \
+ db.security_group_get_by_instance(ctxt,
+ instance['id']):
+ security_groups.add(security_group)
+
+ sg_chain_name = self._security_group_chain_name(security_group)
+
+ our_rules += ['-A %s -j %s' % (chain_name, sg_chain_name)]
+
+ # Allow DHCP responses
+ dhcp_server = self._dhcp_server_for_instance(instance)
+ our_rules += ['-A %s -s %s -p udp --sport 67 --dport 68' %
+ (chain_name, dhcp_server)]
+
+ # If nothing matches, jump to the fallback chain
+ our_rules += ['-A %s -j nova-ipv4-fallback' % (chain_name,)]
+
+ # then, security group chains and rules
+ for security_group in security_groups:
+ chain_name = self._security_group_chain_name(security_group)
+ our_chains += [':%s - [0:0]' % chain_name]
+
+ rules = \
+ db.security_group_rule_get_by_security_group(ctxt,
+ security_group['id'])
+
+ for rule in rules:
+ logging.info('%r', rule)
+ args = ['-A', chain_name, '-p', rule.protocol]
+
+ if rule.cidr:
+ args += ['-s', rule.cidr]
+ else:
+ # Eventually, a mechanism to grant access for security
+ # groups will turn up here. It'll use ipsets.
+ continue
+
+ if rule.protocol in ['udp', 'tcp']:
+ if rule.from_port == rule.to_port:
+ args += ['--dport', '%s' % (rule.from_port,)]
+ else:
+ args += ['-m', 'multiport',
+ '--dports', '%s:%s' % (rule.from_port,
+ rule.to_port)]
+ elif rule.protocol == 'icmp':
+ icmp_type = rule.from_port
+ icmp_code = rule.to_port
+
+ if icmp_type == '-1':
+ icmp_type_arg = None
+ else:
+ icmp_type_arg = '%s' % icmp_type
+ if not icmp_code == '-1':
+ icmp_type_arg += '/%s' % icmp_code
+
+ if icmp_type_arg:
+ args += ['-m', 'icmp', '--icmp_type', icmp_type_arg]
+
+ args += ['-j ACCEPT']
+ our_rules += [' '.join(args)]
+
+ new_filter[rules_index:rules_index] = our_rules
+ new_filter[rules_index:rules_index] = our_chains
+ logging.info('new_filter: %s', '\n'.join(new_filter))
+ return new_filter
+
+ def refresh_security_group_members(self, security_group):
+ pass
+
+ def refresh_security_group_rules(self, security_group):
+ self.apply_ruleset()
+
+ def _security_group_chain_name(self, security_group):
+ return 'nova-sg-%s' % (security_group['id'],)
+
+ def _instance_chain_name(self, instance):
+ return 'nova-inst-%s' % (instance['id'],)
+
+ def _ip_for_instance(self, instance):
+ return db.instance_get_fixed_address(context.get_admin_context(),
+ instance['id'])
+
+ def _dhcp_server_for_instance(self, instance):
+ network = db.project_get_network(context.get_admin_context(),
+ instance['project_id'])
+ return network['gateway']
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index aa4026f97..96d8f5fc8 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -52,12 +52,12 @@ A fake XenAPI SDK.
import datetime
-import logging
import uuid
from pprint import pformat
from nova import exception
+from nova import log as logging
_CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\
@@ -65,9 +65,11 @@ _CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\
_db_content = {}
+LOG = logging.getLogger("nova.virt.xenapi.fake")
+
def log_db_contents(msg=None):
- logging.debug(_("%s: _db_content => %s"), msg or "", pformat(_db_content))
+ LOG.debug(_("%s: _db_content => %s"), msg or "", pformat(_db_content))
def reset():
@@ -242,9 +244,9 @@ class SessionBase(object):
full_params = (self._session,) + params
meth = getattr(self, methodname, None)
if meth is None:
- logging.warn('Raising NotImplemented')
+ LOG.debug(_('Raising NotImplemented'))
raise NotImplementedError(
- 'xenapi.fake does not have an implementation for %s' %
+ _('xenapi.fake does not have an implementation for %s') %
methodname)
return meth(*full_params)
@@ -278,12 +280,12 @@ class SessionBase(object):
if impl is not None:
def callit(*params):
- logging.warn('Calling %s %s', name, impl)
+ LOG.debug(_('Calling %s %s'), name, impl)
self._check_session(params)
return impl(*params)
return callit
if self._is_gettersetter(name, True):
- logging.warn('Calling getter %s', name)
+ LOG.debug(_('Calling getter %s'), name)
return lambda *params: self._getter(name, params)
elif self._is_create(name):
return lambda *params: self._create(name, params)
@@ -333,10 +335,10 @@ class SessionBase(object):
field in _db_content[cls][ref]):
return _db_content[cls][ref][field]
- logging.error('Raising NotImplemented')
+ LOG.debuug(_('Raising NotImplemented'))
raise NotImplementedError(
- 'xenapi.fake does not have an implementation for %s or it has '
- 'been called with the wrong number of arguments' % name)
+ _('xenapi.fake does not have an implementation for %s or it has '
+ 'been called with the wrong number of arguments') % name)
def _setter(self, name, params):
self._check_session(params)
@@ -351,7 +353,7 @@ class SessionBase(object):
field in _db_content[cls][ref]):
_db_content[cls][ref][field] = val
- logging.warn('Raising NotImplemented')
+ LOG.debug(_('Raising NotImplemented'))
raise NotImplementedError(
'xenapi.fake does not have an implementation for %s or it has '
'been called with the wrong number of arguments or the database '
@@ -399,7 +401,7 @@ class SessionBase(object):
self._session not in _db_content['session']):
raise Failure(['HANDLE_INVALID', 'session', self._session])
if len(params) == 0 or params[0] != self._session:
- logging.warn('Raising NotImplemented')
+ LOG.debug(_('Raising NotImplemented'))
raise NotImplementedError('Call to XenAPI without using .xenapi')
def _check_arg_count(self, params, expected):
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 9d1b51848..1e9448a26 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -19,7 +19,6 @@ Helper methods for operations related to the management of VM records and
their attributes like VDIs, VIFs, as well as their lookup functions.
"""
-import logging
import pickle
import urllib
from xml.dom import minidom
@@ -27,6 +26,7 @@ from xml.dom import minidom
from eventlet import event
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
from nova.auth.manager import AuthManager
from nova.compute import instance_types
@@ -37,6 +37,7 @@ from nova.virt.xenapi.volume_utils import StorageError
FLAGS = flags.FLAGS
+LOG = logging.getLogger("nova.virt.xenapi.vm_utils")
XENAPI_POWER_STATE = {
'Halted': power_state.SHUTDOWN,
@@ -121,9 +122,9 @@ class VMHelper(HelperBase):
rec['HVM_boot_params'] = {'order': 'dc'}
rec['platform'] = {'acpi': 'true', 'apic': 'true',
'pae': 'true', 'viridian': 'true'}
- logging.debug('Created VM %s...', instance.name)
+ LOG.debug(_('Created VM %s...'), instance.name)
vm_ref = session.call_xenapi('VM.create', rec)
- logging.debug(_('Created VM %s as %s.'), instance.name, vm_ref)
+ LOG.debug(_('Created VM %s as %s.'), instance.name, vm_ref)
return vm_ref
@classmethod
@@ -143,10 +144,9 @@ class VMHelper(HelperBase):
vbd_rec['qos_algorithm_type'] = ''
vbd_rec['qos_algorithm_params'] = {}
vbd_rec['qos_supported_algorithms'] = []
- logging.debug(_('Creating VBD for VM %s, VDI %s ... '),
- vm_ref, vdi_ref)
+ LOG.debug(_('Creating VBD for VM %s, VDI %s ... '), vm_ref, vdi_ref)
vbd_ref = session.call_xenapi('VBD.create', vbd_rec)
- logging.debug(_('Created VBD %s for VM %s, VDI %s.'), vbd_ref, vm_ref,
+ LOG.debug(_('Created VBD %s for VM %s, VDI %s.'), vbd_ref, vm_ref,
vdi_ref)
return vbd_ref
@@ -161,7 +161,7 @@ class VMHelper(HelperBase):
if vbd_rec['userdevice'] == str(number):
return vbd
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('VBD not found in instance %s') % vm_ref)
@classmethod
@@ -170,7 +170,7 @@ class VMHelper(HelperBase):
try:
vbd_ref = session.call_xenapi('VBD.unplug', vbd_ref)
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
if exc.details[0] != 'DEVICE_ALREADY_DETACHED':
raise StorageError(_('Unable to unplug VBD %s') % vbd_ref)
@@ -183,7 +183,7 @@ class VMHelper(HelperBase):
#with Josh Kearney
session.wait_for_task(0, task)
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to destroy VBD %s') % vbd_ref)
@classmethod
@@ -199,11 +199,11 @@ class VMHelper(HelperBase):
vif_rec['other_config'] = {}
vif_rec['qos_algorithm_type'] = ''
vif_rec['qos_algorithm_params'] = {}
- logging.debug(_('Creating VIF for VM %s, network %s.'), vm_ref,
- network_ref)
+ LOG.debug(_('Creating VIF for VM %s, network %s.'), vm_ref,
+ network_ref)
vif_ref = session.call_xenapi('VIF.create', vif_rec)
- logging.debug(_('Created VIF %s for VM %s, network %s.'), vif_ref,
- vm_ref, network_ref)
+ LOG.debug(_('Created VIF %s for VM %s, network %s.'), vif_ref,
+ vm_ref, network_ref)
return vif_ref
@classmethod
@@ -213,8 +213,7 @@ class VMHelper(HelperBase):
"""
#TODO(sirp): Add quiesce and VSS locking support when Windows support
# is added
- logging.debug(_("Snapshotting VM %s with label '%s'..."),
- vm_ref, label)
+ LOG.debug(_("Snapshotting VM %s with label '%s'..."), vm_ref, label)
vm_vdi_ref, vm_vdi_rec = get_vdi_for_vm_safely(session, vm_ref)
vm_vdi_uuid = vm_vdi_rec["uuid"]
@@ -227,8 +226,8 @@ class VMHelper(HelperBase):
template_vdi_rec = get_vdi_for_vm_safely(session, template_vm_ref)[1]
template_vdi_uuid = template_vdi_rec["uuid"]
- logging.debug(_('Created snapshot %s from VM %s.'), template_vm_ref,
- vm_ref)
+ LOG.debug(_('Created snapshot %s from VM %s.'), template_vm_ref,
+ vm_ref)
parent_uuid = wait_for_vhd_coalesce(
session, instance_id, sr_ref, vm_vdi_ref, original_parent_uuid)
@@ -241,8 +240,7 @@ class VMHelper(HelperBase):
""" Requests that the Glance plugin bundle the specified VDIs and
push them into Glance using the specified human-friendly name.
"""
- logging.debug(_("Asking xapi to upload %s as '%s'"),
- vdi_uuids, image_name)
+ LOG.debug(_("Asking xapi to upload %s as '%s'"), vdi_uuids, image_name)
params = {'vdi_uuids': vdi_uuids,
'image_name': image_name,
@@ -260,7 +258,7 @@ class VMHelper(HelperBase):
"""
url = images.image_url(image)
access = AuthManager().get_access_key(user, project)
- logging.debug("Asking xapi to fetch %s as %s", url, access)
+ LOG.debug(_("Asking xapi to fetch %s as %s"), url, access)
fn = (type != ImageType.KERNEL_RAMDISK) and 'get_vdi' or 'get_kernel'
args = {}
args['src_url'] = url
@@ -278,7 +276,7 @@ class VMHelper(HelperBase):
@classmethod
def lookup_image(cls, session, vdi_ref):
- logging.debug("Looking up vdi %s for PV kernel", vdi_ref)
+ LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref)
fn = "is_vdi_pv"
args = {}
args['vdi-ref'] = vdi_ref
@@ -289,7 +287,7 @@ class VMHelper(HelperBase):
pv = True
elif pv_str.lower() == 'false':
pv = False
- logging.debug("PV Kernel in VDI:%d", pv)
+ LOG.debug(_("PV Kernel in VDI:%d"), pv)
return pv
@classmethod
@@ -317,10 +315,9 @@ class VMHelper(HelperBase):
vdi = session.get_xenapi().VBD.get_VDI(vbd)
# Test valid VDI
record = session.get_xenapi().VDI.get_record(vdi)
- logging.debug(_('VDI %s is still available'),
- record['uuid'])
+ LOG.debug(_('VDI %s is still available'), record['uuid'])
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
else:
vdis.append(vdi)
if len(vdis) > 0:
@@ -331,10 +328,10 @@ class VMHelper(HelperBase):
@classmethod
def compile_info(cls, record):
"""Fill record with VM status information"""
- logging.info(_("(VM_UTILS) xenserver vm state -> |%s|"),
- record['power_state'])
- logging.info(_("(VM_UTILS) xenapi power_state -> |%s|"),
- XENAPI_POWER_STATE[record['power_state']])
+ LOG.info(_("(VM_UTILS) xenserver vm state -> |%s|"),
+ record['power_state'])
+ LOG.info(_("(VM_UTILS) xenapi power_state -> |%s|"),
+ XENAPI_POWER_STATE[record['power_state']])
return {'state': XENAPI_POWER_STATE[record['power_state']],
'max_mem': long(record['memory_static_max']) >> 10,
'mem': long(record['memory_dynamic_max']) >> 10,
@@ -388,11 +385,9 @@ def get_vhd_parent(session, vdi_rec):
"""
if 'vhd-parent' in vdi_rec['sm_config']:
parent_uuid = vdi_rec['sm_config']['vhd-parent']
- #NOTE(sirp): changed xenapi -> get_xenapi()
parent_ref = session.get_xenapi().VDI.get_by_uuid(parent_uuid)
parent_rec = session.get_xenapi().VDI.get_record(parent_ref)
- #NOTE(sirp): changed log -> logging
- logging.debug(_("VHD %s has parent %s"), vdi_rec['uuid'], parent_ref)
+ LOG.debug(_("VHD %s has parent %s"), vdi_rec['uuid'], parent_ref)
return parent_ref, parent_rec
else:
return None
@@ -409,7 +404,7 @@ def get_vhd_parent_uuid(session, vdi_ref):
def scan_sr(session, instance_id, sr_ref):
- logging.debug(_("Re-scanning SR %s"), sr_ref)
+ LOG.debug(_("Re-scanning SR %s"), sr_ref)
task = session.call_xenapi('Async.SR.scan', sr_ref)
session.wait_for_task(instance_id, task)
@@ -433,10 +428,9 @@ def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
scan_sr(session, instance_id, sr_ref)
parent_uuid = get_vhd_parent_uuid(session, vdi_ref)
if original_parent_uuid and (parent_uuid != original_parent_uuid):
- logging.debug(
- _("Parent %s doesn't match original parent %s, "
- "waiting for coalesce..."),
- parent_uuid, original_parent_uuid)
+ LOG.debug(_("Parent %s doesn't match original parent %s, "
+ "waiting for coalesce..."), parent_uuid,
+ original_parent_uuid)
else:
done.send(parent_uuid)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index b6d620782..e20930fe8 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -20,10 +20,10 @@ Management class for VM-related functions (spawn, reboot, etc).
"""
import json
-import logging
from nova import db
from nova import context
+from nova import log as logging
from nova import exception
from nova import utils
@@ -33,6 +33,9 @@ from nova.virt.xenapi.network_utils import NetworkHelper
from nova.virt.xenapi.vm_utils import VMHelper
from nova.virt.xenapi.vm_utils import ImageType
+XenAPI = None
+LOG = logging.getLogger("nova.virt.xenapi.vmops")
+
class VMOps(object):
"""
@@ -93,10 +96,9 @@ class VMOps(object):
if network_ref:
VMHelper.create_vif(self._session, vm_ref,
network_ref, instance.mac_address)
- logging.debug(_('Starting VM %s...'), vm_ref)
+ LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
- logging.info(_('Spawning VM %s created %s.'), instance.name,
- vm_ref)
+ LOG.info(_('Spawning VM %s created %s.'), instance.name, vm_ref)
# NOTE(armando): Do we really need to do this in virt?
timer = utils.LoopingCall(f=None)
@@ -107,12 +109,12 @@ class VMOps(object):
db.instance_set_state(context.get_admin_context(),
instance['id'], state)
if state == power_state.RUNNING:
- logging.debug(_('Instance %s: booted'), instance['name'])
+ LOG.debug(_('Instance %s: booted'), instance['name'])
timer.stop()
except Exception, exc:
- logging.warn(exc)
- logging.exception(_('instance %s: failed to boot'),
- instance['name'])
+ LOG.warn(exc)
+ LOG.exception(_('instance %s: failed to boot'),
+ instance['name'])
db.instance_set_state(context.get_admin_context(),
instance['id'],
power_state.SHUTDOWN)
@@ -205,7 +207,7 @@ class VMOps(object):
task = self._session.call_xenapi('Async.VM.hard_shutdown', vm)
self._session.wait_for_task(instance.id, task)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
# Disk clean-up
if vdis:
@@ -214,20 +216,20 @@ class VMOps(object):
task = self._session.call_xenapi('Async.VDI.destroy', vdi)
self._session.wait_for_task(instance.id, task)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
# VM Destroy
try:
task = self._session.call_xenapi('Async.VM.destroy', vm)
self._session.wait_for_task(instance.id, task)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
def _wait_with_callback(self, instance_id, task, callback):
ret = None
try:
ret = self._session.wait_for_task(instance_id, task)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
callback(ret)
def pause(self, instance, callback):
diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py
index 4bbc41b03..0cd15b950 100644
--- a/nova/virt/xenapi/volume_utils.py
+++ b/nova/virt/xenapi/volume_utils.py
@@ -21,16 +21,17 @@ and storage repositories
import re
import string
-import logging
from nova import db
from nova import context
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
from nova.virt.xenapi import HelperBase
FLAGS = flags.FLAGS
+LOG = logging.getLogger("nova.virt.xenapi.volume_utils")
class StorageError(Exception):
@@ -53,7 +54,7 @@ class VolumeHelper(HelperBase):
"""
sr_ref = session.get_xenapi().SR.get_by_name_label(label)
if len(sr_ref) == 0:
- logging.debug('Introducing %s...', label)
+ LOG.debug(_('Introducing %s...'), label)
record = {}
if 'chapuser' in info and 'chappassword' in info:
record = {'target': info['targetHost'],
@@ -70,10 +71,10 @@ class VolumeHelper(HelperBase):
session.get_xenapi_host(),
record,
'0', label, description, 'iscsi', '', False, {})
- logging.debug('Introduced %s as %s.', label, sr_ref)
+ LOG.debug(_('Introduced %s as %s.'), label, sr_ref)
return sr_ref
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to create Storage Repository'))
else:
return sr_ref[0]
@@ -85,32 +86,32 @@ class VolumeHelper(HelperBase):
vdi_ref = session.get_xenapi().VBD.get_VDI(vbd_ref)
sr_ref = session.get_xenapi().VDI.get_SR(vdi_ref)
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to find SR from VBD %s') % vbd_ref)
return sr_ref
@classmethod
def destroy_iscsi_storage(cls, session, sr_ref):
"""Forget the SR whilst preserving the state of the disk"""
- logging.debug("Forgetting SR %s ... ", sr_ref)
+ LOG.debug(_("Forgetting SR %s ... "), sr_ref)
pbds = []
try:
pbds = session.get_xenapi().SR.get_PBDs(sr_ref)
except cls.XenAPI.Failure, exc:
- logging.warn('Ignoring exception %s when getting PBDs for %s',
- exc, sr_ref)
+ LOG.warn(_('Ignoring exception %s when getting PBDs for %s'),
+ exc, sr_ref)
for pbd in pbds:
try:
session.get_xenapi().PBD.unplug(pbd)
except cls.XenAPI.Failure, exc:
- logging.warn('Ignoring exception %s when unplugging PBD %s',
- exc, pbd)
+ LOG.warn(_('Ignoring exception %s when unplugging PBD %s'),
+ exc, pbd)
try:
session.get_xenapi().SR.forget(sr_ref)
- logging.debug("Forgetting SR %s done.", sr_ref)
+ LOG.debug(_("Forgetting SR %s done."), sr_ref)
except cls.XenAPI.Failure, exc:
- logging.warn('Ignoring exception %s when forgetting SR %s',
- exc, sr_ref)
+ LOG.warn(_('Ignoring exception %s when forgetting SR %s'), exc,
+ sr_ref)
@classmethod
def introduce_vdi(cls, session, sr_ref):
@@ -118,12 +119,12 @@ class VolumeHelper(HelperBase):
try:
vdis = session.get_xenapi().SR.get_VDIs(sr_ref)
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to introduce VDI on SR %s') % sr_ref)
try:
vdi_rec = session.get_xenapi().VDI.get_record(vdis[0])
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to get record'
' of VDI %s on') % vdis[0])
else:
@@ -141,7 +142,7 @@ class VolumeHelper(HelperBase):
vdi_rec['xenstore_data'],
vdi_rec['sm_config'])
except cls.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise StorageError(_('Unable to introduce VDI for SR %s')
% sr_ref)
@@ -165,11 +166,8 @@ class VolumeHelper(HelperBase):
target_host = _get_target_host(iscsi_portal)
target_port = _get_target_port(iscsi_portal)
target_iqn = _get_iqn(iscsi_name, volume_id)
- logging.debug('(vol_id,number,host,port,iqn): (%s,%s,%s,%s)',
- volume_id,
- target_host,
- target_port,
- target_iqn)
+ LOG.debug('(vol_id,number,host,port,iqn): (%s,%s,%s,%s)',
+ volume_id, target_host, target_port, target_iqn)
if (device_number < 0) or \
(volume_id is None) or \
(target_host is None) or \
@@ -196,7 +194,7 @@ class VolumeHelper(HelperBase):
elif re.match('^[0-9]+$', mountpoint):
return string.atoi(mountpoint, 10)
else:
- logging.warn('Mountpoint cannot be translated: %s', mountpoint)
+ LOG.warn(_('Mountpoint cannot be translated: %s'), mountpoint)
return -1
@@ -257,7 +255,7 @@ def _get_target(volume_id):
"sendtargets -p %s" %
volume_ref['host'])
except exception.ProcessExecutionError, exc:
- logging.warn(exc)
+ LOG.exception(exc)
else:
targets = r.splitlines()
if len(_e) == 0 and len(targets) == 1:
diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py
index fdeb2506c..189f968c6 100644
--- a/nova/virt/xenapi/volumeops.py
+++ b/nova/virt/xenapi/volumeops.py
@@ -17,14 +17,17 @@
"""
Management class for Storage-related functions (attach, detach, etc).
"""
-import logging
from nova import exception
+from nova import log as logging
from nova.virt.xenapi.vm_utils import VMHelper
from nova.virt.xenapi.volume_utils import VolumeHelper
from nova.virt.xenapi.volume_utils import StorageError
+LOG = logging.getLogger("nova.virt.xenapi.volumeops")
+
+
class VolumeOps(object):
"""
Management class for Volume-related tasks
@@ -45,8 +48,8 @@ class VolumeOps(object):
raise exception.NotFound(_('Instance %s not found')
% instance_name)
# NOTE: No Resource Pool concept so far
- logging.debug(_("Attach_volume: %s, %s, %s"),
- instance_name, device_path, mountpoint)
+ LOG.debug(_("Attach_volume: %s, %s, %s"),
+ instance_name, device_path, mountpoint)
# Create the iSCSI SR, and the PDB through which hosts access SRs.
# But first, retrieve target info, like Host, IQN, LUN and SCSIID
vol_rec = VolumeHelper.parse_volume_info(device_path, mountpoint)
@@ -61,7 +64,7 @@ class VolumeOps(object):
try:
vdi_ref = VolumeHelper.introduce_vdi(self._session, sr_ref)
except StorageError, exc:
- logging.warn(exc)
+ LOG.exception(exc)
VolumeHelper.destroy_iscsi_storage(self._session, sr_ref)
raise Exception(_('Unable to create VDI on SR %s for instance %s')
% (sr_ref,
@@ -73,7 +76,7 @@ class VolumeOps(object):
vol_rec['deviceNumber'],
False)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
VolumeHelper.destroy_iscsi_storage(self._session, sr_ref)
raise Exception(_('Unable to use SR %s for instance %s')
% (sr_ref,
@@ -84,13 +87,13 @@ class VolumeOps(object):
vbd_ref)
self._session.wait_for_task(vol_rec['deviceNumber'], task)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.exception(exc)
VolumeHelper.destroy_iscsi_storage(self._session,
sr_ref)
raise Exception(_('Unable to attach volume to instance %s')
% instance_name)
- logging.info(_('Mountpoint %s attached to instance %s'),
- mountpoint, instance_name)
+ LOG.info(_('Mountpoint %s attached to instance %s'),
+ mountpoint, instance_name)
def detach_volume(self, instance_name, mountpoint):
"""Detach volume storage to VM instance"""
@@ -100,13 +103,13 @@ class VolumeOps(object):
raise exception.NotFound(_('Instance %s not found')
% instance_name)
# Detach VBD from VM
- logging.debug(_("Detach_volume: %s, %s"), instance_name, mountpoint)
+ LOG.debug(_("Detach_volume: %s, %s"), instance_name, mountpoint)
device_number = VolumeHelper.mountpoint_to_number(mountpoint)
try:
vbd_ref = VMHelper.find_vbd_by_number(self._session,
vm_ref, device_number)
except StorageError, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise Exception(_('Unable to locate volume %s') % mountpoint)
else:
try:
@@ -114,13 +117,13 @@ class VolumeOps(object):
vbd_ref)
VMHelper.unplug_vbd(self._session, vbd_ref)
except StorageError, exc:
- logging.warn(exc)
+ LOG.exception(exc)
raise Exception(_('Unable to detach volume %s') % mountpoint)
try:
VMHelper.destroy_vbd(self._session, vbd_ref)
except StorageError, exc:
- logging.warn(exc)
+ LOG.exception(exc)
# Forget SR
VolumeHelper.destroy_iscsi_storage(self._session, sr_ref)
- logging.info(_('Mountpoint %s detached from instance %s'),
- mountpoint, instance_name)
+ LOG.info(_('Mountpoint %s detached from instance %s'),
+ mountpoint, instance_name)
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index c48f5b7cb..b8ab9245f 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -51,7 +51,6 @@ reactor thread if the VM.get_by_name_label or VM.get_record calls block.
:iqn_prefix: IQN Prefix, e.g. 'iqn.2010-10.org.openstack'
"""
-import logging
import sys
import xmlrpclib
@@ -62,9 +61,14 @@ from nova import context
from nova import db
from nova import utils
from nova import flags
+from nova import log as logging
from nova.virt.xenapi.vmops import VMOps
from nova.virt.xenapi.volumeops import VolumeOps
+
+LOG = logging.getLogger("nova.virt.xenapi")
+
+
FLAGS = flags.FLAGS
flags.DEFINE_string('xenapi_connection_url',
@@ -194,6 +198,7 @@ class XenAPISession(object):
self.XenAPI = self.get_imported_xenapi()
self._session = self._create_session(url)
self._session.login_with_password(user, pw)
+ self.loop = None
def get_imported_xenapi(self):
"""Stubout point. This can be replaced with a mock xenapi module."""
@@ -230,14 +235,20 @@ class XenAPISession(object):
def wait_for_task(self, id, task):
"""Return the result of the given task. The task is polled
- until it completes."""
+ until it completes. Not re-entrant."""
done = event.Event()
- loop = utils.LoopingCall(self._poll_task, id, task, done)
- loop.start(FLAGS.xenapi_task_poll_interval, now=True)
+ self.loop = utils.LoopingCall(self._poll_task, id, task, done)
+ self.loop.start(FLAGS.xenapi_task_poll_interval, now=True)
rv = done.wait()
- loop.stop()
+ self.loop.stop()
return rv
+ def _stop_loop(self):
+ """Stop polling for task to finish."""
+ #NOTE(sandy-walsh) Had to break this call out to support unit tests.
+ if self.loop:
+ self.loop.stop()
+
def _create_session(self, url):
"""Stubout point. This can be replaced with a mock session."""
return self.XenAPI.Session(url)
@@ -256,7 +267,7 @@ class XenAPISession(object):
return
elif status == "success":
result = self._session.xenapi.task.get_result(task)
- logging.info(_("Task [%s] %s status: success %s") % (
+ LOG.info(_("Task [%s] %s status: success %s") % (
name,
task,
result))
@@ -264,7 +275,7 @@ class XenAPISession(object):
else:
error_info = self._session.xenapi.task.get_error_info(task)
action["error"] = str(error_info)
- logging.warn(_("Task [%s] %s status: %s %s") % (
+ LOG.warn(_("Task [%s] %s status: %s %s") % (
name,
task,
status,
@@ -272,15 +283,16 @@ class XenAPISession(object):
done.send_exception(self.XenAPI.Failure(error_info))
db.instance_action_create(context.get_admin_context(), action)
except self.XenAPI.Failure, exc:
- logging.warn(exc)
+ LOG.warn(exc)
done.send_exception(*sys.exc_info())
+ self._stop_loop()
def _unwrap_plugin_exceptions(self, func, *args, **kwargs):
"""Parse exception details"""
try:
return func(*args, **kwargs)
except self.XenAPI.Failure, exc:
- logging.debug(_("Got exception: %s"), exc)
+ LOG.debug(_("Got exception: %s"), exc)
if (len(exc.details) == 4 and
exc.details[0] == 'XENAPI_PLUGIN_EXCEPTION' and
exc.details[2] == 'Failure'):
@@ -293,7 +305,7 @@ class XenAPISession(object):
else:
raise
except xmlrpclib.ProtocolError, exc:
- logging.debug(_("Got exception: %s"), exc)
+ LOG.debug(_("Got exception: %s"), exc)
raise
diff --git a/nova/volume/api.py b/nova/volume/api.py
index 2d7fe3762..ce4831cc3 100644
--- a/nova/volume/api.py
+++ b/nova/volume/api.py
@@ -21,11 +21,11 @@ Handles all requests relating to volumes.
"""
import datetime
-import logging
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import quota
from nova import rpc
from nova.db import base
@@ -33,16 +33,18 @@ from nova.db import base
FLAGS = flags.FLAGS
flags.DECLARE('storage_availability_zone', 'nova.volume.manager')
+LOG = logging.getLogger('nova.volume')
+
class API(base.Base):
"""API for interacting with the volume manager."""
def create(self, context, size, name, description):
if quota.allowed_volumes(context, 1, size) < 1:
- logging.warn("Quota exceeeded for %s, tried to create %sG volume",
+ LOG.warn(_("Quota exceeeded for %s, tried to create %sG volume"),
context.project_id, size)
- raise quota.QuotaError("Volume quota exceeded. You cannot "
- "create a volume of size %s" % size)
+ raise quota.QuotaError(_("Volume quota exceeded. You cannot "
+ "create a volume of size %s") % size)
options = {
'size': size,
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index 8353b9712..6bc925f3e 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -20,15 +20,15 @@ Drivers for volumes.
"""
-import logging
-import os
import time
from nova import exception
from nova import flags
+from nova import log as logging
from nova import utils
+LOG = logging.getLogger("nova.volume.driver")
FLAGS = flags.FLAGS
flags.DEFINE_string('volume_group', 'nova-volumes',
'Name for the VG that will contain exported volumes')
@@ -73,13 +73,15 @@ class VolumeDriver(object):
tries = tries + 1
if tries >= FLAGS.num_shell_tries:
raise
- logging.exception(_("Recovering from a failed execute."
- "Try number %s"), tries)
+ LOG.exception(_("Recovering from a failed execute. "
+ "Try number %s"), tries)
time.sleep(tries ** 2)
def check_for_setup_error(self):
"""Returns an error if prerequisites aren't met"""
- if not os.path.isdir("/dev/%s" % FLAGS.volume_group):
+ out, err = self._execute("sudo vgs --noheadings -o name")
+ volume_groups = out.split()
+ if not FLAGS.volume_group in volume_groups:
raise exception.Error(_("volume group %s doesn't exist")
% FLAGS.volume_group)
@@ -205,7 +207,7 @@ class FakeAOEDriver(AOEDriver):
@staticmethod
def fake_execute(cmd, *_args, **_kwargs):
"""Execute that simply logs the command."""
- logging.debug(_("FAKE AOE: %s"), cmd)
+ LOG.debug(_("FAKE AOE: %s"), cmd)
return (None, None)
@@ -310,5 +312,5 @@ class FakeISCSIDriver(ISCSIDriver):
@staticmethod
def fake_execute(cmd, *_args, **_kwargs):
"""Execute that simply logs the command."""
- logging.debug(_("FAKE ISCSI: %s"), cmd)
+ LOG.debug(_("FAKE ISCSI: %s"), cmd)
return (None, None)
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 966334c50..6348539c5 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -42,17 +42,18 @@ intact.
"""
-import logging
import datetime
from nova import context
from nova import exception
from nova import flags
+from nova import log as logging
from nova import manager
from nova import utils
+LOG = logging.getLogger('nova.volume.manager')
FLAGS = flags.FLAGS
flags.DEFINE_string('storage_availability_zone',
'nova',
@@ -81,7 +82,7 @@ class VolumeManager(manager.Manager):
self.driver.check_for_setup_error()
ctxt = context.get_admin_context()
volumes = self.db.volume_get_all_by_host(ctxt, self.host)
- logging.debug(_("Re-exporting %s volumes"), len(volumes))
+ LOG.debug(_("Re-exporting %s volumes"), len(volumes))
for volume in volumes:
self.driver.ensure_export(ctxt, volume)
@@ -89,7 +90,7 @@ class VolumeManager(manager.Manager):
"""Creates and exports the volume."""
context = context.elevated()
volume_ref = self.db.volume_get(context, volume_id)
- logging.info(_("volume %s: creating"), volume_ref['name'])
+ LOG.info(_("volume %s: creating"), volume_ref['name'])
self.db.volume_update(context,
volume_id,
@@ -98,18 +99,18 @@ class VolumeManager(manager.Manager):
# before passing it to the driver.
volume_ref['host'] = self.host
- logging.debug(_("volume %s: creating lv of size %sG"),
- volume_ref['name'], volume_ref['size'])
+ LOG.debug(_("volume %s: creating lv of size %sG"), volume_ref['name'],
+ volume_ref['size'])
self.driver.create_volume(volume_ref)
- logging.debug(_("volume %s: creating export"), volume_ref['name'])
+ LOG.debug(_("volume %s: creating export"), volume_ref['name'])
self.driver.create_export(context, volume_ref)
now = datetime.datetime.utcnow()
self.db.volume_update(context,
volume_ref['id'], {'status': 'available',
'launched_at': now})
- logging.debug(_("volume %s: created successfully"), volume_ref['name'])
+ LOG.debug(_("volume %s: created successfully"), volume_ref['name'])
return volume_id
def delete_volume(self, context, volume_id):
@@ -120,12 +121,12 @@ class VolumeManager(manager.Manager):
raise exception.Error(_("Volume is still attached"))
if volume_ref['host'] != self.host:
raise exception.Error(_("Volume is not local to this node"))
- logging.debug(_("volume %s: removing export"), volume_ref['name'])
+ LOG.debug(_("volume %s: removing export"), volume_ref['name'])
self.driver.remove_export(context, volume_ref)
- logging.debug(_("volume %s: deleting"), volume_ref['name'])
+ LOG.debug(_("volume %s: deleting"), volume_ref['name'])
self.driver.delete_volume(volume_ref)
self.db.volume_destroy(context, volume_id)
- logging.debug(_("volume %s: deleted successfully"), volume_ref['name'])
+ LOG.debug(_("volume %s: deleted successfully"), volume_ref['name'])
return True
def setup_compute_volume(self, context, volume_id):
diff --git a/nova/wsgi.py b/nova/wsgi.py
index b5d6b96c1..e999f76a3 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -22,7 +22,6 @@ Utility methods for working with WSGI servers
"""
import json
-import logging
import sys
from xml.dom import minidom
@@ -35,18 +34,30 @@ import webob
import webob.dec
import webob.exc
+from nova import log as logging
-logging.getLogger("routes.middleware").addHandler(logging.StreamHandler())
+
+class WritableLogger(object):
+ """A thin wrapper that responds to `write` and logs."""
+
+ def __init__(self, logger, level=logging.DEBUG):
+ self.logger = logger
+ self.level = level
+
+ def write(self, msg):
+ self.logger.log(self.level, msg)
class Server(object):
"""Server class to manage multiple WSGI sockets and applications."""
def __init__(self, threads=1000):
+ logging.basicConfig()
self.pool = eventlet.GreenPool(threads)
def start(self, application, port, host='0.0.0.0', backlog=128):
"""Run a WSGI server with the given application."""
+ logging.audit(_("Starting %s on %s:%s"), sys.argv[0], host, port)
socket = eventlet.listen((host, port), backlog=backlog)
self.pool.spawn_n(self._run, application, socket)
@@ -59,7 +70,9 @@ class Server(object):
def _run(self, application, socket):
"""Start a WSGI server in a new green thread."""
- eventlet.wsgi.server(socket, application, custom_pool=self.pool)
+ logger = logging.getLogger('eventlet.wsgi.server')
+ eventlet.wsgi.server(socket, application, custom_pool=self.pool,
+ log=WritableLogger(logger))
class Application(object):
diff --git a/setup.cfg b/setup.cfg
index 14dcb5c8e..9c0a331e3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,3 +8,17 @@ tag_build =
tag_date = 0
tag_svn_revision = 0
+[compile_catalog]
+directory = locale
+domain = nova
+
+[update_catalog]
+domain = nova
+output_dir = locale
+input_file = locale/nova.pot
+
+[extract_messages]
+keywords = _ l_ lazy_gettext
+mapping_file = babel.cfg
+output_file = locale/nova.pot
+
diff --git a/setup.py b/setup.py
index 1abf4d9fe..3608ff805 100644
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,15 @@ from setuptools.command.sdist import sdist
from sphinx.setup_command import BuildDoc
from nova.utils import parse_mailmap, str_dict_replace
+from nova import version
+
+if os.path.isdir('.bzr'):
+ with open("nova/vcsversion.py", 'w') as version_file:
+ vcs_cmd = subprocess.Popen(["bzr", "version-info", "--python"],
+ stdout=subprocess.PIPE)
+ vcsversion = vcs_cmd.communicate()[0]
+ version_file.write(vcsversion)
+
class local_BuildDoc(BuildDoc):
def run(self):
@@ -48,14 +57,25 @@ class local_sdist(sdist):
changelog_file.write(str_dict_replace(changelog, mailmap))
sdist.run(self)
+nova_cmdclass= { 'sdist': local_sdist,
+ 'build_sphinx' : local_BuildDoc }
+
+try:
+ from babel.messages import frontend as babel
+ nova_cmdclass['compile_catalog'] = babel.compile_catalog
+ nova_cmdclass['extract_messages'] = babel.extract_messages
+ nova_cmdclass['init_catalog'] = babel.init_catalog
+ nova_cmdclass['update_catalog'] = babel.update_catalog
+except:
+ pass
+
setup(name='nova',
- version='2011.1',
+ version=version.canonical_version_string(),
description='cloud computing fabric controller',
author='OpenStack',
author_email='nova@lists.launchpad.net',
url='http://www.openstack.org/',
- cmdclass={ 'sdist': local_sdist,
- 'build_sphinx' : local_BuildDoc },
+ cmdclass=nova_cmdclass,
packages=find_packages(exclude=['bin', 'smoketests']),
include_package_data=True,
test_suite='nose.collector',
@@ -64,9 +84,11 @@ setup(name='nova',
'bin/nova-dhcpbridge',
'bin/nova-import-canonical-imagestore',
'bin/nova-instancemonitor',
+ 'bin/nova-logspool',
'bin/nova-manage',
'bin/nova-network',
'bin/nova-objectstore',
'bin/nova-scheduler',
+ 'bin/nova-spoolsentry',
'bin/nova-volume',
'tools/nova-debug'])
diff --git a/smoketests/admin_smoketests.py b/smoketests/admin_smoketests.py
index 50bb3fa2e..1ef1c1425 100644
--- a/smoketests/admin_smoketests.py
+++ b/smoketests/admin_smoketests.py
@@ -19,10 +19,17 @@
import os
import random
import sys
-import time
import unittest
import zipfile
+# If ../nova/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ sys.path.insert(0, possible_topdir)
+
from nova import adminclient
from smoketests import flags
from smoketests import base
diff --git a/smoketests/user_smoketests.py b/smoketests/user_smoketests.py
index d29e3aea3..578c0722e 100644
--- a/smoketests/user_smoketests.py
+++ b/smoketests/user_smoketests.py
@@ -24,6 +24,14 @@ import sys
import time
import unittest
+# If ../nova/__init__.py exists, add ../ to Python search path, so that
+# it will override what happens to be installed in /usr/(local/)lib/python...
+possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
+ os.pardir,
+ os.pardir))
+if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
+ sys.path.insert(0, possible_topdir)
+
from smoketests import flags
from smoketests import base
@@ -40,6 +48,7 @@ flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
TEST_PREFIX = 'test%s' % int (random.random()*1000000)
TEST_BUCKET = '%s_bucket' % TEST_PREFIX
TEST_KEY = '%s_key' % TEST_PREFIX
+TEST_GROUP = '%s_group' % TEST_PREFIX
TEST_DATA = {}
@@ -137,7 +146,7 @@ class InstanceTests(UserSmokeTestCase):
self.data['instance_id'] = reservation.instances[0].id
def test_003_instance_runs_within_60_seconds(self):
- reservations = self.conn.get_all_instances([data['instance_id']])
+ reservations = self.conn.get_all_instances([self.data['instance_id']])
instance = reservations[0].instances[0]
# allow 60 seconds to exit pending with IP
for x in xrange(60):
@@ -207,7 +216,7 @@ class InstanceTests(UserSmokeTestCase):
def test_999_tearDown(self):
self.delete_key_pair(self.conn, TEST_KEY)
if self.data.has_key('instance_id'):
- self.conn.terminate_instances([data['instance_id']])
+ self.conn.terminate_instances([self.data['instance_id']])
class VolumeTests(UserSmokeTestCase):
@@ -319,8 +328,80 @@ class VolumeTests(UserSmokeTestCase):
self.conn.delete_key_pair(TEST_KEY)
+class SecurityGroupTests(UserSmokeTestCase):
+
+ def __public_instance_is_accessible(self):
+ id_url = "latest/meta-data/instance-id"
+ options = "-s --max-time 1"
+ command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url)
+ instance_id = commands.getoutput(command).strip()
+ if not instance_id:
+ return False
+ if instance_id != self.data['instance_id']:
+ raise Exception("Wrong instance id")
+ return True
+
+ def test_001_can_create_security_group(self):
+ self.conn.create_security_group(TEST_GROUP, description='test')
+
+ groups = self.conn.get_all_security_groups()
+ self.assertTrue(TEST_GROUP in [group.name for group in groups])
+
+ def test_002_can_launch_instance_in_security_group(self):
+ self.create_key_pair(self.conn, TEST_KEY)
+ reservation = self.conn.run_instances(FLAGS.test_image,
+ key_name=TEST_KEY,
+ security_groups=[TEST_GROUP],
+ instance_type='m1.tiny')
+
+ self.data['instance_id'] = reservation.instances[0].id
+
+ def test_003_can_authorize_security_group_ingress(self):
+ self.assertTrue(self.conn.authorize_security_group(TEST_GROUP,
+ ip_protocol='tcp',
+ from_port=80,
+ to_port=80))
+
+ def test_004_can_access_instance_over_public_ip(self):
+ result = self.conn.allocate_address()
+ self.assertTrue(hasattr(result, 'public_ip'))
+ self.data['public_ip'] = result.public_ip
+
+ result = self.conn.associate_address(self.data['instance_id'],
+ self.data['public_ip'])
+ start_time = time.time()
+ while not self.__public_instance_is_accessible():
+ # 1 minute to launch
+ if time.time() - start_time > 60:
+ raise Exception("Timeout")
+ time.sleep(1)
+
+ def test_005_can_revoke_security_group_ingress(self):
+ self.assertTrue(self.conn.revoke_security_group(TEST_GROUP,
+ ip_protocol='tcp',
+ from_port=80,
+ to_port=80))
+ start_time = time.time()
+ while self.__public_instance_is_accessible():
+ # 1 minute to teardown
+ if time.time() - start_time > 60:
+ raise Exception("Timeout")
+ time.sleep(1)
+
+
+ def test_999_tearDown(self):
+ self.conn.delete_key_pair(TEST_KEY)
+ self.conn.delete_security_group(TEST_GROUP)
+ groups = self.conn.get_all_security_groups()
+ self.assertFalse(TEST_GROUP in [group.name for group in groups])
+ self.conn.terminate_instances([self.data['instance_id']])
+ self.assertTrue(self.conn.release_address(self.data['public_ip']))
+
+
if __name__ == "__main__":
suites = {'image': unittest.makeSuite(ImageTests),
'instance': unittest.makeSuite(InstanceTests),
- 'volume': unittest.makeSuite(VolumeTests)}
+ 'security_group': unittest.makeSuite(SecurityGroupTests),
+ 'volume': unittest.makeSuite(VolumeTests)
+ }
sys.exit(base.run_tests(suites))