diff options
294 files changed, 3812 insertions, 2007 deletions
diff --git a/.gitignore b/.gitignore index d61dac1f1..94661beec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,32 @@ -*.pyc -*.DS_Store -local_settings.py +.autogenerated +.coverage +.nova-venv +.project +.pydevproject +.tox +.venv +AUTHORS +Authors +CA/ ChangeLog MANIFEST -CA/ -keeper -instances -keys -build/* build-stamp -nova.egg-info -.nova-venv -.venv -.tox -*.sqlite -*.log -*.mo -nosetests.xml +build/* coverage.xml -tools/conf/nova.conf* cover/* +covhtml +dist/* +instances +keeper +keys +local_settings.py +nosetests.xml nova/tests/cover/* nova/vcsversion.py -.autogenerated -dist/* -.coverage -covhtml +tools/conf/nova.conf* +*.log +*.mo +*.pyc +*.sqlite +*.DS_Store +*.egg* diff --git a/Authors b/Authors deleted file mode 100644 index e13d824ee..000000000 --- a/Authors +++ /dev/null @@ -1,235 +0,0 @@ -Aaron Lee <aaron.lee@rackspace.com> -Adam Gandelman <adamg@canonical.com> -Adam Johnson <adjohn@gmail.com> -Adrian Smith <adrian_f_smith@dell.com> -Ahmad Hassan <ahmad.hassan@hp.com> -Alessio Ababilov <aababilov@griddynamics.com> -Alex Meade <alex.meade@rackspace.com> -Alexander Sakhnov <asakhnov@mirantis.com> -Alexander Kovalev <akovalev@mirantis.com> -Alvaro Lopez Garcia <aloga@ifca.unican.es> -Andrew Bogott <abogott@wikimedia.org> -Andrew Clay Shafer <acs@parvuscaptus.com> -Andrey Brindeyev <abrindeyev@griddynamics.com> -Andy Smith <code@term.ie> -Andy Southgate <andy.southgate@citrix.com> -Anne Gentle <anne@openstack.org> -Ante Karamatić <ivoks@ubuntu.com> -Anthony Young <sleepsonthefloor@gmail.com> -Antony Messerli <ant@openstack.org> -Armando Migliaccio <Armando.Migliaccio@eu.citrix.com> -Arvind Somya <asomya@cisco.com> -Asbjørn Sannes <asbjorn.sannes@interhost.no> -Ben McGraw <ben@pistoncloud.com> -Ben Swartzlander <bswartz@netapp.com> -Bilal Akhtar <bilalakhtar@ubuntu.com> -Boris Filippov <bfilippov@griddynamics.com> -Brad Hall <brad@nicira.com> -Brad McConnell <bmcconne@rackspace.com> -Brendan Maguire <B_Maguire@Dell.com> -Brian Elliott <brian.elliott@rackspace.com> -Brian Lamar <brian.lamar@rackspace.com> -Brian Schott <bschott@isi.edu> -Brian Waldon <brian.waldon@rackspace.com> -Chiradeep Vittal <chiradeep@cloud.com> -Chmouel Boudjnah <chmouel@chmouel.com> -Chris Behrens <cbehrens@codestud.com> -Christian Berendt <berendt@b1-systems.de> -Chris Fattarsi <chris.fattarsi@pistoncloud.com> -Christopher MacGown <chris@pistoncloud.com> -Chuck Short <zulcss@ubuntu.com> -Cole Robinson <crobinso@redhat.com> -Cor Cornelisse <cor@hyves.nl> -Cory Wright <corywright@gmail.com> -Craig Vyvial <cp16net@gmail.com> -Dan Prince <dprince@redhat.com> -Dan Wendlandt <dan@nicira.com> -Dan Smith <danms@us.ibm.com> -Daniel P. Berrange <berrange@redhat.com> -Dave Lapsley <dlapsley@nicira.com> -Dave Walker <Dave.Walker@canonical.com> -David Pravec <David.Pravec@danix.org> -David Scannell <dscannell@gridcentric.com> -David Shrewsbury <shrewsbury.dave@gmail.com> -David Subiros <david.perez5@hp.com> -Dean Troyer <dtroyer@gmail.com> -Deepak Garg <deepak.garg@citrix.com> -Deevi Rani <deevi_rani@persistent.co.in> -Derek Higgins <derekh@redhat.com> -Devananda van der Veen <devananda.vdv@gmail.com> -Devdeep Singh <devdeep.singh@citrix.com> -Devendra Modium <dmodium@isi.edu> -Devin Carlen <devin.carlen@gmail.com> -Dina Belova <dbelova@mirantis.com> -Dominik Heidler <dheidler@suse.de> -Don Dugger <donald.d.dugger@intel.com> -Donal Lafferty <donal.lafferty@citrix.com> -Dong-In David Kang <dkang@isi.edu> -Doug Hellmann <doug.hellmann@dreamhost.com> -Duncan McGreggor <duncan@dreamhost.com> -Ed Leafe <ed@leafe.com> -Edouard Thuleau <edouard1.thuleau@orange.com> -Eldar Nugaev <reldan@oscloud.ru> -Eoghan Glynn <eglynn@redhat.com> -Eric Day <eday@oddments.org> -Eric Harney <eharney@gmail.com> -Eric Windisch <eric@cloudscaling.com> -Evan Callicoat <diopter@gmail.com> -Ewan Mellor <ewan.mellor@citrix.com> -Florian Haas <florian@hastexo.com> -François Charlier <francois.charlier@enovance.com> -Gabe Westmaas <gabe.westmaas@rackspace.com> -Gabriel Hurley <gabriel@strikeawe.com> -Gary Kotton <garyk@radware.com> -Gaurav Gupta <gaurav@denali-systems.com> -Greg Althaus <galthaus@austin.rr.com> -Hengqing Hu <hudayou@hotmail.com> -Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp> -Hisaki Ohara <hisaki.ohara@intel.com> -Hua Zhang <zhhuabj@cn.ibm.com> -Ilya Alekseyev <ilyaalekseyev@acm.org> -Ionuț Arțăriși <iartarisi@suse.cz> -Isaku Yamahata <yamahata@valinux.co.jp> -Ivan Kolodyazhny <e0ne@e0ne.info> -J. Daniel Schmidt <jdsn@suse.de> -Jake Dahn <jake@ansolabs.com> -James E. Blair <jeblair@hp.com> -Jason Cannavale <jason.cannavale@rackspace.com> -Jason Koelker <jason@koelker.net> -Jay Pipes <jaypipes@gmail.com> -JC Martin <jcmartin@ebaysf.com> -Jesse Andrews <anotherjesse@gmail.com> -Jim Fehlig <jfehlig@suse.com> -Jimmy Bergman <jimmy@sigint.se> -Joe Gordon <jogo@cloudscaling.com> -Joe Heck <heckj@mac.com> -Joel Moore <joelbm24@gmail.com> -Johannes Erdfelt <johannes.erdfelt@rackspace.com> -John Dewey <john@dewey.ws> -John Garbutt <john.garbutt@citrix.com> -John Griffith <john.griffith@solidfire.com> -John Kennedy <john.m.kennedy@intel.com> -John Tran <jhtran@att.com> -Jonathan Bryce <jbryce@jbryce.com> -Jordan Rinke <jordan@openstack.org> -Joseph Suh <jsuh@isi.edu> -Joseph W. Breu <breu@breu.org> -Josh Durgin <joshd@hq.newdream.net> -Josh Kearney <josh@jk0.org> -Josh Kleinpeter <josh@kleinpeter.org> -Joshua Harlow <harlowja@yahoo-inc.com> -Joshua McKenty <jmckenty@gmail.com> -Juan G. Hernando Rivero <ghe@debian.org> -Julien Danjou <julien.danjou@enovance.com> -Justin Santa Barbara <justin@fathomdb.com> -Justin Shepherd <jshepher@rackspace.com> -Kei Masumoto <masumotok@nttdata.co.jp> -Keisuke Tagami <tagami.keisuke@lab.ntt.co.jp> -masumoto<masumotok@nttdata.co.jp> -masukotm<masukotm@nttdata.co.jp> -Ken Pepple <ken.pepple@gmail.com> -Kevin Bringard <kbringard@attinteractive.com> -Kevin L. Mitchell <kevin.mitchell@rackspace.com> -Kiall Mac Innes <kiall@managedit.ie> -Kirill Shileev <kshileev@gmail.com> -Koji Iida <iida.koji@lab.ntt.co.jp> -Liam Kelleher <liam.kelleher@hp.com> -Likitha Shetty <likitha.shetty@citrix.com> -Lin Hua Cheng <lin-hua.cheng@hp.com> -Loganathan Parthipan <parthipan@hp.com> -Lorin Hochstein <lorin@nimbisservices.com> -Lvov Maxim <usrleon@gmail.com> -Mandar Vaze <mandar.vaze@vertex.co.in> -Mandell Degerness <mdegerne@gmail.com> -Mark McClain <mark.mcclain@dreamhost.com> -Mark McLoughlin <markmc@redhat.com> -Mark Washenberger <mark.washenberger@rackspace.com> -Martin Packman <martin.packman@canonical.com> -Maru Newby <mnewby@internap.com> -Masanori Itoh <itoumsn@nttdata.co.jp> -Matt Dietz <matt.dietz@rackspace.com> -Matt Stephenson <mattstep@mattstep.net> -Matthew Hooker <matt@cloudscaling.com> -Matthew Joyce <matt.joyce@cloudscaling.com> -Michael Basnight <mbasnigh@rackspace.com> -Michael Gundlach <michael.gundlach@rackspace.com> -Michael Still <mikal@stillhq.com> -Mike Lundy <mike@pistoncloud.com> -Mike Milner <mike.milner@canonical.com> -Mike Pittaro <mikeyp@lahondaresearch.org> -Mike Scherbakov <mihgen@gmail.com> -Mikyung Kang <mkkang@isi.edu> -Mohammed Naser <mnaser@vexxhost.com> -Monsyne Dragon <mdragon@rackspace.com> -Monty Taylor <mordred@inaugust.com> -MORITA Kazutaka <morita.kazutaka@gmail.com> -MotoKen <motokentsai@gmail.com> -Muneyuki Noguchi <noguchimn@nttdata.co.jp> -Nachi Ueno <ueno.nachi@lab.ntt.co.jp> -Naveed Massjouni <naveedm9@gmail.com> -Nick Bartos <nick@pistoncloud.com> -Nikhil Komawar <nikhil.komawar@rackspace.com> -Nikolay Sokolov <nsokolov@griddynamics.com> -Nirmal Ranganathan <rnirmal@gmail.com> -Ollie Leahy <oliver.leahy@hp.com> -Pádraig Brady <pbrady@redhat.com> -Paul McMillan <paul.mcmillan@nebula.com> -Paul Voccio <paul@openstack.org> -Peng Yong <ppyy@pubyun.com> -Philip Knouff <philip.knouff@mailtrust.com> -Ralf Haferkamp <rhafer@suse.de> -Renier Morales <renierm@us.ibm.com> -Renuka Apte <renuka.apte@citrix.com> -Ricardo Carrillo Cruz <emaildericky@gmail.com> -Rick Clark <rick@openstack.org> -Rick Harris <rconradharris@gmail.com> -Rob Kost <kost@isi.edu> -Robert Esker <esker@netapp.com> -Russell Bryant <rbryant@redhat.com> -Russell Sim <russell.sim@gmail.com> -Ryan Lane <rlane@wikimedia.org> -Ryan Lucio <rlucio@internap.com> -Ryu Ishimoto <ryu@midokura.jp> -Salvatore Orlando <salvatore.orlando@eu.citrix.com> -Sandy Walsh <sandy.walsh@rackspace.com> -Sascha Peilicke <saschpe@suse.de> -Sateesh Chodapuneedi <sateesh.chodapuneedi@citrix.com> -Scott Moser <smoser@ubuntu.com> -Sean Dague <sdague@linux.vnet.ibm.com> -Sean M. Collins <sean@coreitpro.com> -Somik Behera <somikbehera@gmail.com> -Soren Hansen <soren.hansen@rackspace.com> -Stanislaw Pitucha <stanislaw.pitucha@hp.com> -Stephanie Reese <reese.sm@gmail.com> -Steve Baker <steve@stevebaker.org> -Sumit Naiksatam <snaiksat@cisco.com> -Thierry Carrez <thierry@openstack.org> -Tim Simpson <tim.simpson@rackspace.com> -Todd Willey <todd@ansolabs.com> -Tomoe Sugihara <tomoe@midokura.com> -Tomoya Masuko<masukotm@nttdata.co.jp> -Thorsten Tarrach <thorsten@atomia.com> -Trey Morris <trey.morris@rackspace.com> -Troy Toman <troy.toman@rackspace.com> -Tushar Patil <tushar.vitthal.patil@gmail.com> -Unmesh Gurjar <unmesh.gurjar@vertex.co.in> -Vasiliy Shlykov <vash@vasiliyshlykov.org> -Vishvananda Ishaya <vishvananda@gmail.com> -Vivek Y S <vivek.ys@gmail.com> -Vladimir Popovski <vladimir@zadarastorage.com> -Vaddi kiran <vaddi_kiran@persistent.co.in> -William Henry <whenry@redhat.com> -William Kelly <william.kelly@rackspace.com> -William Wolf <throughnothing@gmail.com> -Yaguang Tang <heut2008@gmail.com> -Ying Chun Guo <daisy.ycguo@gmail.com> -Yoshiaki Tamura <yoshi@midokura.jp> -Youcef Laribi <Youcef.Laribi@eu.citrix.com> -Yun Mao <yunmao@gmail.com> -Yun Shen <Yun.Shen@hp.com> -Yuriy Taraday <yorik.sar@gmail.com> -Zed Shaw <zedshaw@zedshaw.com> -Zhiteng Huang <zhiteng.huang@intel.com> -Zhixue Wu <Zhixue.Wu@citrix.com> -Zhongyue Luo <lzyeval@gmail.com> -Ziad Sawalha <github@highbridgellc.com> diff --git a/HACKING.rst b/HACKING.rst index c85f5f305..2ebc58927 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -213,3 +213,24 @@ with the help of openstack-common's update.py script. See: The copy of the code should never be directly modified here. Please always update openstack-common first and then run the script to copy the changes across. + +Commit Messages +--------------- +Using a common format for commit messages will help keep our git history +readable. Follow these guidelines: + + First, provide a brief summary (limited to 50 chars). + + The first line of the commit message should provide an accurate + description of the change, not just a reference to a bug or + blueprint. It must be followed by a single blank line. + + Following your brief summary, provide a more detailed description of + the patch, manually wrapping the text at 72 characters. This + description should provide enough detail that one does not have to + refer to external resources to determine its high-level functionality. + + Once you use 'git review', two lines will be appended to the commit + message: a blank line followed by a 'Change-Id'. This is important + to correlate this commit with a specific review in Gerrit, and it + should not be modified. diff --git a/MANIFEST.in b/MANIFEST.in index 050ca7e0e..c978a52da 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include Authors +include AUTHORS include ChangeLog exclude .gitignore exclude .gitreview diff --git a/README.rst b/README.rst index ddd42240e..e39c83ed7 100644 --- a/README.rst +++ b/README.rst @@ -1,22 +1,58 @@ -The Choose Your Own Adventure README for Nova -============================================= +OpenStack Nova README +===================== -You have come across a cloud computing fabric controller. It has identified -itself as "Nova." It is apparent that it maintains compatibility with -the popular Amazon EC2 and S3 APIs. +OpenStack Nova provides a cloud computing fabric controller, +supporting a wide variety of virtualization technologies, +including KVM, Xen, LXC, VMWare, and more. In addition to +its native API, it includes compatibility with the commonly +encountered Amazon EC2 and S3 APIs. -To monitor it from a distance: follow `@openstack <http://twitter.com/openstack>`_ on twitter. +OpenStack Nova is distributed under the terms of the Apache +License, Version 2.0. The full terms and conditions of this +license are detailed in the LICENSE file. -To tame it for use in your own cloud: read http://docs.openstack.org +Nova primarily consists of a set of Python daemons, though +it requires and integrates with a number of native system +components for databases, messaging and virtualization +capabilities. -To study its anatomy: read http://nova.openstack.org +To keep updated with new developments in the OpenStack project +follow `@openstack <http://twitter.com/openstack>`_ on Twitter. -To dissect it in detail: visit http://github.com/openstack/nova +To learn how to deploy OpenStack Nova, consult the documentation +available online at: -To taunt it with its weaknesses: use http://bugs.launchpad.net/nova + http://docs.openstack.org -To watch it: http://jenkins.openstack.org +In the unfortunate event that bugs are discovered, they should +be reported to the appropriate bug tracker. If you obtained +the software from a 3rd party operating system vendor, it is +often wise to use their own bug tracker for reporting problems. +In all other cases use the master OpenStack bug tracker, +available at: -To hack at it: read HACKING + http://bugs.launchpad.net/nova -To cry over its PEP-8 problems: http://jenkins.openstack.org/job/gate-nova-pep8/violations +Developers wishing to work on the OpenStack Nova project should +always base their work on the latest Nova code, available from +the master GIT repository at: + + http://github.com/openstack/nova + +Developers should also join the dicussion on the mailing list, +at: + + https://lists.launchpad.net/openstack/ + +Any new code must follow the development guidelines detailed +in the HACKING.rst file, and pass all unit tests. Further +developer focused documentation is available at: + + http://nova.openstack.org/ + +Changes to OpenStack Nova should be submitted for review via +the Gerrit tool, following the workflow documented at: + + http://wiki.openstack.org/GerritWorkflow + +-- End of broadcast diff --git a/bin/nova-all b/bin/nova-all index 6aee87805..a67c77b99 100755 --- a/bin/nova-all +++ b/bin/nova-all @@ -41,8 +41,8 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging from nova.objectstore import s3server +from nova.openstack.common import log as logging from nova import service from nova import utils from nova.vnc import xvp_proxy @@ -52,7 +52,7 @@ LOG = logging.getLogger('nova.all') if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() launcher = service.ProcessLauncher() diff --git a/bin/nova-api b/bin/nova-api index b778854f0..f55eca719 100755 --- a/bin/nova-api +++ b/bin/nova-api @@ -37,13 +37,13 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() launcher = service.ProcessLauncher() for api in flags.FLAGS.enabled_apis: diff --git a/bin/nova-api-ec2 b/bin/nova-api-ec2 index f76f3dd7e..8e66ab874 100755 --- a/bin/nova-api-ec2 +++ b/bin/nova-api-ec2 @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.WSGIService('ec2') service.serve(server, workers=server.workers) diff --git a/bin/nova-api-metadata b/bin/nova-api-metadata index 2aad93453..d445a16a9 100755 --- a/bin/nova-api-metadata +++ b/bin/nova-api-metadata @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.WSGIService('metadata') service.serve(server, workers=server.workers) diff --git a/bin/nova-api-os-compute b/bin/nova-api-os-compute index c83855254..529d58821 100755 --- a/bin/nova-api-os-compute +++ b/bin/nova-api-os-compute @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.WSGIService('osapi_compute') service.serve(server, workers=server.workers) diff --git a/bin/nova-api-os-volume b/bin/nova-api-os-volume index f08c69384..7c368fce9 100755 --- a/bin/nova-api-os-volume +++ b/bin/nova-api-os-volume @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.WSGIService('osapi_volume') service.serve(server, workers=server.workers) diff --git a/bin/nova-cert b/bin/nova-cert index a845c1055..81189492a 100755 --- a/bin/nova-cert +++ b/bin/nova-cert @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.Service.create(binary='nova-cert') service.serve(server) diff --git a/bin/nova-clear-rabbit-queues b/bin/nova-clear-rabbit-queues index aff9da14e..a2484c0b3 100755 --- a/bin/nova-clear-rabbit-queues +++ b/bin/nova-clear-rabbit-queues @@ -43,8 +43,8 @@ gettext.install('nova', unicode=1) from nova import context from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import rpc @@ -70,7 +70,7 @@ def delete_queues(queues): if __name__ == '__main__': args = flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") delete_queues(args[1:]) if FLAGS.delete_exchange: delete_exchange(FLAGS.control_exchange) diff --git a/bin/nova-compute b/bin/nova-compute index bef7bce86..0a8432b1e 100755 --- a/bin/nova-compute +++ b/bin/nova-compute @@ -35,13 +35,13 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup('nova') utils.monkey_patch() server = service.Service.create(binary='nova-compute') service.serve(server) diff --git a/bin/nova-console b/bin/nova-console index f5a760b37..a40e84c92 100755 --- a/bin/nova-console +++ b/bin/nova-console @@ -34,12 +34,12 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") server = service.Service.create(binary='nova-console') service.serve(server) service.wait() diff --git a/bin/nova-consoleauth b/bin/nova-consoleauth index 71d77b285..082c7f6a2 100755 --- a/bin/nova-consoleauth +++ b/bin/nova-consoleauth @@ -33,13 +33,13 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova.consoleauth import manager from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service if __name__ == "__main__": flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") server = service.Service.create(binary='nova-consoleauth') service.serve(server) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index d5179d074..0693ae27a 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -38,9 +38,9 @@ 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.network import linux_net from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import utils @@ -97,7 +97,7 @@ def main(): """Parse environment and arguments and call the approproate action.""" flagfile = os.environ.get('FLAGFILE', FLAGS.dhcpbridge_flagfile) argv = flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") if int(os.environ.get('TESTING', '0')): from nova.tests import fake_flags diff --git a/bin/nova-instance-usage-audit b/bin/nova-instance-usage-audit index 9fc71db05..1a7f34d6b 100755 --- a/bin/nova-instance-usage-audit +++ b/bin/nova-instance-usage-audit @@ -55,7 +55,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.openstack.common import log as logging from nova.openstack.common import rpc from nova import utils @@ -65,7 +65,7 @@ FLAGS = flags.FLAGS if __name__ == '__main__': admin_context = context.get_admin_context() flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") begin, end = utils.last_completed_audit_period() print "Starting instance usage audit" print "Creating usages for %s until %s" % (str(begin), str(end)) diff --git a/bin/nova-manage b/bin/nova-manage index bc9dd4cd4..e3fd5e11a 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -61,7 +61,6 @@ import math import netaddr import optparse import os -import StringIO import sys @@ -81,15 +80,13 @@ from nova.compat import flagfile from nova.compute import instance_types from nova.compute import rpcapi as compute_rpcapi from nova import context -from nova import crypto from nova import db from nova.db import migration from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils -from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import quota @@ -219,98 +216,6 @@ class ShellCommands(object): arguments: path""" exec(compile(open(path).read(), path, 'exec'), locals(), globals()) - @args('--filename', dest='filename', metavar='<path>', default=False, - help='Export file path') - def export(self, filename): - """Export Nova users into a file that can be consumed by Keystone""" - - def create_file(filename): - data = generate_data() - with open(filename, 'w') as f: - f.write(data.getvalue()) - - def tenants(data, am): - for project in am.get_projects(): - print >> data, ("tenant add '%s'" % - (project.name)) - for u in project.member_ids: - user = am.get_user(u) - print >> data, ("user add '%s' '%s' '%s'" % - (user.name, user.access, project.name)) - print >> data, ("credentials add 'EC2' '%s:%s' '%s' '%s'" % - (user.access, project.id, user.secret, project.id)) - - def roles(data, am): - for role in am.get_roles(): - print >> data, ("role add '%s'" % (role)) - - def grant_roles(data, am): - roles = am.get_roles() - for project in am.get_projects(): - for u in project.member_ids: - user = am.get_user(u) - for role in db.user_get_roles_for_project(ctxt, u, - project.id): - print >> data, ("role grant '%s', '%s', '%s')," % - (user.name, role, project.name)) - print >> data - - def generate_data(): - data = StringIO.StringIO() - am = manager.AuthManager() - tenants(data, am) - roles(data, am) - grant_roles(data, am) - data.seek(0) - return data - - ctxt = context.get_admin_context() - if filename: - create_file(filename) - else: - data = generate_data() - print data.getvalue() - - -class RoleCommands(object): - """Class for managing roles.""" - - def __init__(self): - self.manager = manager.AuthManager() - - @args('--user', dest="user", metavar='<user name>', help='User name') - @args('--role', dest="role", metavar='<user role>', help='User role') - @args('--project', dest="project", metavar='<Project name>', - help='Project name') - def add(self, user, role, project=None): - """adds role to user - if project is specified, adds project specific role""" - if project: - projobj = self.manager.get_project(project) - if not projobj.has_member(user): - print "%s not a member of %s" % (user, project) - return - self.manager.add_role(user, role, project) - - @args('--user', dest="user", metavar='<user name>', help='User name') - @args('--role', dest="role", metavar='<user role>', help='User role') - @args('--project', dest="project", metavar='<Project name>', - help='Project name') - def has(self, user, role, project=None): - """checks to see if user has role - if project is specified, returns True if user has - the global role and the project role""" - print self.manager.has_role(user, role, project) - - @args('--user', dest="user", metavar='<user name>', help='User name') - @args('--role', dest="role", metavar='<user role>', help='User role') - @args('--project', dest="project", metavar='<Project name>', - help='Project name') - def remove(self, user, role, project=None): - """removes role from user - if project is specified, removes project specific role""" - self.manager.remove_role(user, role, project) - def _db_error(caught_exception): print caught_exception @@ -320,174 +225,12 @@ def _db_error(caught_exception): exit(1) -class UserCommands(object): - """Class for managing users.""" - - @staticmethod - def _print_export(user): - """Print export variables to use with API.""" - print 'export EC2_ACCESS_KEY=%s' % user.access - print 'export EC2_SECRET_KEY=%s' % user.secret - - def __init__(self): - self.manager = manager.AuthManager() - - @args('--name', dest="name", metavar='<admin name>', help='Admin name') - @args('--access', dest="access", metavar='<access>', help='Access') - @args('--secret', dest="secret", metavar='<secret>', help='Secret') - def admin(self, name, access=None, secret=None): - """creates a new admin and prints exports""" - try: - user = self.manager.create_user(name, access, secret, True) - except exception.DBError, e: - _db_error(e) - self._print_export(user) - - @args('--name', dest="name", metavar='<name>', help='User name') - @args('--access', dest="access", metavar='<access>', help='Access') - @args('--secret', dest="secret", metavar='<secret>', help='Secret') - def create(self, name, access=None, secret=None): - """creates a new user and prints exports""" - try: - user = self.manager.create_user(name, access, secret, False) - except exception.DBError, e: - _db_error(e) - self._print_export(user) - - @args('--name', dest="name", metavar='<name>', help='User name') - def delete(self, name): - """deletes an existing user - arguments: name""" - self.manager.delete_user(name) - - @args('--name', dest="name", metavar='<admin name>', help='User name') - def exports(self, name): - """prints access and secrets for user in export format""" - user = self.manager.get_user(name) - if user: - self._print_export(user) - else: - print "User %s doesn't exist" % name - - def list(self): - """lists all users""" - for user in self.manager.get_users(): - print user.name - - @args('--name', dest="name", metavar='<name>', help='User name') - @args('--access', dest="access_key", metavar='<access>', - help='Access key') - @args('--secret', dest="secret_key", metavar='<secret>', - help='Secret key') - @args('--is_admin', dest='is_admin', metavar="<'T'|'F'>", - help='Is admin?') - def modify(self, name, access_key, secret_key, is_admin): - """update a users keys & admin flag - arguments: accesskey secretkey admin - leave any field blank to ignore it, admin should be 'T', 'F', or blank - """ - if not is_admin: - is_admin = None - elif is_admin.upper()[0] == 'T': - is_admin = True - else: - is_admin = False - self.manager.modify_user(name, access_key, secret_key, is_admin) - - @args('--name', dest="user_id", metavar='<name>', help='User name') - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') - def revoke(self, user_id, project_id=None): - """revoke certs for a user""" - if project_id: - crypto.revoke_certs_by_user_and_project(user_id, project_id) - else: - crypto.revoke_certs_by_user(user_id) - - class ProjectCommands(object): """Class for managing projects.""" def __init__(self): self.manager = manager.AuthManager() - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') - @args('--user', dest="user_id", metavar='<name>', help='User name') - def add(self, project_id, user_id): - """Adds user to project""" - try: - self.manager.add_to_project(user_id, project_id) - except exception.UserNotFound as ex: - print ex - raise - - @args('--project', dest="name", metavar='<Project name>', - help='Project name') - @args('--user', dest="project_manager", metavar='<user>', - help='Project manager') - @args('--desc', dest="description", metavar='<description>', - help='Description') - def create(self, name, project_manager, description=None): - """Creates a new project""" - try: - self.manager.create_project(name, project_manager, description) - except exception.UserNotFound as ex: - print ex - raise - - @args('--project', dest="name", metavar='<Project name>', - help='Project name') - @args('--user', dest="project_manager", metavar='<user>', - help='Project manager') - @args('--desc', dest="description", metavar='<description>', - help='Description') - def modify(self, name, project_manager, description=None): - """Modifies a project""" - try: - self.manager.modify_project(name, project_manager, description) - except exception.UserNotFound as ex: - print ex - raise - - @args('--project', dest="name", metavar='<Project name>', - help='Project name') - def delete(self, name): - """Deletes an existing project""" - try: - self.manager.delete_project(name) - except exception.ProjectNotFound as ex: - print ex - raise - - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') - @args('--user', dest="user_id", metavar='<name>', help='User name') - @args('--file', dest="filename", metavar='<filename>', - help='File name(Default: novarc)') - def environment(self, project_id, user_id, filename='novarc'): - """Exports environment variables to a sourceable file""" - try: - rc = self.manager.get_environment_rc(user_id, project_id) - except (exception.UserNotFound, exception.ProjectNotFound) as ex: - print ex - raise - if filename == "-": - sys.stdout.write(rc) - else: - with open(filename, 'w') as f: - f.write(rc) - - @args('--user', dest="username", metavar='<username>', help='User name') - def list(self, username=None): - """Lists all projects""" - for project in self.manager.get_projects(username): - print project.name - - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') - @args('--key', dest="key", metavar='<key>', help='Key') - @args('--value', dest="value", metavar='<value>', help='Value') def quota(self, project_id, key=None, value=None): """Set or display quotas for project""" ctxt = context.get_admin_context() @@ -506,17 +249,6 @@ class ProjectCommands(object): @args('--project', dest="project_id", metavar='<Project name>', help='Project name') - @args('--user', dest="user_id", metavar='<name>', help='User name') - def remove(self, project_id, user_id): - """Removes user from project""" - try: - self.manager.remove_from_project(user_id, project_id) - except (exception.UserNotFound, exception.ProjectNotFound) as ex: - print ex - raise - - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') def scrub(self, project_id): """Deletes data associated with project""" admin_context = context.get_admin_context() @@ -527,32 +259,6 @@ class ProjectCommands(object): for group in groups: db.security_group_destroy(admin_context, group['id']) - @args('--project', dest="project_id", metavar='<Project name>', - help='Project name') - @args('--user', dest="user_id", metavar='<name>', help='User name') - @args('--file', dest="filename", metavar='<filename>', - help='File name(Default: nova.zip)') - def zipfile(self, project_id, user_id, filename='nova.zip'): - """Exports credentials for project to a zip file""" - try: - zip_file = self.manager.get_credentials(user_id, project_id) - if filename == "-": - sys.stdout.write(zip_file) - else: - with open(filename, 'w') as f: - f.write(zip_file) - except (exception.UserNotFound, exception.ProjectNotFound) as ex: - print ex - raise - except db.api.NoMoreNetworks: - print _('No more networks available. If this is a new ' - 'installation, you need\nto call something like this:\n\n' - ' nova-manage network create pvt 10.0.0.0/8 10 64\n\n') - except exception.ProcessExecutionError, e: - print e - print _("The above error may show that the certificate db has " - "not been created.\nPlease create a database by running " - "a nova-cert server on this host.") AccountCommands = ProjectCommands @@ -1531,86 +1237,11 @@ class GetLogCommands(object): print "No nova entries in syslog!" -class ExportCommands(object): - """Commands used to export data from Nova""" - - def auth(self): - """Export Nova auth data in format that can be consumed by Keystone""" - print jsonutils.dumps(self._get_auth_data()) - - def _get_auth_data(self): - output = { - 'users': [], - 'tenants': [], - 'user_tenant_list': [], - 'ec2_credentials': [], - 'roles': [], - 'role_user_tenant_list': [], - } - - am = manager.AuthManager() - - for user in am.get_users(): - # NOTE(vish): Deprecated auth uses an access key, no auth uses a - # the user_id in place of it. - if FLAGS.auth_strategy == 'deprecated': - access = user.access - else: - access = user.id - - user_dict = { - 'id': user.id, - 'name': user.name, - 'password': access, - } - output['users'].append(user_dict) - - ec2_cred = { - 'user_id': user.id, - 'access_key': access, - 'secret_key': user.secret, - } - output['ec2_credentials'].append(ec2_cred) - - for project in am.get_projects(): - tenant = { - 'id': project.id, - 'name': project.name, - 'description': project.description, - } - output['tenants'].append(tenant) - - for user_id in project.member_ids: - membership = { - 'tenant_id': project.id, - 'user_id': user_id, - } - output['user_tenant_list'].append(membership) - - for role in am.get_roles(): - if role not in output['roles']: - output['roles'].append(role) - - for project in am.get_projects(): - for user_id in project.member_ids: - user = am.get_user(user_id) - for role in am.get_user_roles(user_id, project.id): - role_grant = { - 'role': role, - 'user_id': user_id, - 'tenant_id': project.id, - } - output['role_user_tenant_list'].append(role_grant) - - return output - - CATEGORIES = [ ('account', AccountCommands), ('agent', AgentBuildCommands), ('config', ConfigCommands), ('db', DbCommands), - ('export', ExportCommands), ('fixed', FixedIpCommands), ('flavor', InstanceTypeCommands), ('floating', FloatingIpCommands), @@ -1619,11 +1250,9 @@ CATEGORIES = [ ('logs', GetLogCommands), ('network', NetworkCommands), ('project', ProjectCommands), - ('role', RoleCommands), ('service', ServiceCommands), ('shell', ShellCommands), ('sm', StorageManagerCommands), - ('user', UserCommands), ('version', VersionCommands), ('vm', VmCommands), ('volume', VolumeCommands), @@ -1667,7 +1296,7 @@ def main(): try: argv = flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") except cfg.ConfigFilesNotFoundError: cfgfile = FLAGS.config_file[-1] if FLAGS.config_file else None if cfgfile and not os.access(cfgfile, os.R_OK): diff --git a/bin/nova-network b/bin/nova-network index 306eddafa..ca9a97551 100755 --- a/bin/nova-network +++ b/bin/nova-network @@ -35,13 +35,13 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.Service.create(binary='nova-network') service.serve(server) diff --git a/bin/nova-novncproxy b/bin/nova-novncproxy index 30a13612e..2c834d13a 100755 --- a/bin/nova-novncproxy +++ b/bin/nova-novncproxy @@ -30,8 +30,8 @@ import websockify from nova import context from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import rpc from nova import utils diff --git a/bin/nova-objectstore b/bin/nova-objectstore index e9bd0f42d..2149b1610 100755 --- a/bin/nova-objectstore +++ b/bin/nova-objectstore @@ -35,15 +35,15 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova import flags -from nova import log as logging from nova.objectstore import s3server +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = s3server.get_wsgi_server() service.serve(server) diff --git a/bin/nova-rpc-zmq-receiver b/bin/nova-rpc-zmq-receiver index 2fe569b77..926eee377 100755 --- a/bin/nova-rpc-zmq-receiver +++ b/bin/nova-rpc-zmq-receiver @@ -1,4 +1,4 @@ -#!/usr/bin/env python -d +#!/usr/bin/env python # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2011 OpenStack LLC @@ -19,7 +19,6 @@ import eventlet eventlet.monkey_patch() import contextlib -import logging import os import sys @@ -35,6 +34,7 @@ if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')): from nova import exception from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common.rpc import impl_zmq from nova import utils @@ -45,7 +45,7 @@ CONF = cfg.CONF def main(): CONF.register_opts(rpc.rpc_opts) impl_zmq.register_opts(CONF) - logging.setup() + logging.setup("nova") utils.monkey_patch() ipc_dir = CONF.rpc_zmq_ipc_dir diff --git a/bin/nova-scheduler b/bin/nova-scheduler index fb803ab5b..e100156df 100755 --- a/bin/nova-scheduler +++ b/bin/nova-scheduler @@ -37,13 +37,13 @@ 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.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.Service.create(binary='nova-scheduler') service.serve(server) diff --git a/bin/nova-volume b/bin/nova-volume index d8c13b9a6..2c10c99f5 100755 --- a/bin/nova-volume +++ b/bin/nova-volume @@ -35,13 +35,13 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import service from nova import utils if __name__ == '__main__': flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") utils.monkey_patch() server = service.Service.create(binary='nova-volume') service.serve(server) diff --git a/bin/nova-volume-usage-audit b/bin/nova-volume-usage-audit index aff650aa4..735786f8f 100755 --- a/bin/nova-volume-usage-audit +++ b/bin/nova-volume-usage-audit @@ -54,7 +54,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.openstack.common import log as logging from nova.openstack.common import rpc from nova import utils from nova.volume import utils as volume_utils @@ -65,7 +65,7 @@ if __name__ == '__main__': admin_context = context.get_admin_context() utils.default_cfgfile() flags.FLAGS(sys.argv) - logging.setup() + logging.setup("nova") begin, end = utils.last_completed_audit_period() print "Starting volume usage audit" print "Creating usages for %s until %s" % (str(begin), str(end)) diff --git a/bin/nova-xvpvncproxy b/bin/nova-xvpvncproxy index 1e3708072..4a2f0ec11 100755 --- a/bin/nova-xvpvncproxy +++ b/bin/nova-xvpvncproxy @@ -32,7 +32,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')): from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import service from nova.vnc import xvp_proxy @@ -41,7 +41,7 @@ FLAGS = flags.FLAGS if __name__ == "__main__": flags.parse_args(sys.argv) - logging.setup() + logging.setup("nova") wsgi_server = xvp_proxy.get_wsgi_server() service.serve(wsgi_server) diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py index a0cfd7e26..940608ffd 100644 --- a/contrib/boto_v6/ec2/connection.py +++ b/contrib/boto_v6/ec2/connection.py @@ -3,11 +3,11 @@ Created on 2010/12/20 @author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp> ''' -import boto import base64 +import boto import boto.ec2 -from boto_v6.ec2.instance import ReservationV6 from boto.ec2.securitygroup import SecurityGroup +from boto_v6.ec2.instance import ReservationV6 class EC2ConnectionV6(boto.ec2.EC2Connection): diff --git a/contrib/boto_v6/ec2/instance.py b/contrib/boto_v6/ec2/instance.py index 9208c05ad..74adccc00 100644 --- a/contrib/boto_v6/ec2/instance.py +++ b/contrib/boto_v6/ec2/instance.py @@ -3,10 +3,10 @@ Created on 2010/12/20 @author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp> ''' -from boto.resultset import ResultSet -from boto.ec2.instance import Reservation from boto.ec2.instance import Group from boto.ec2.instance import Instance +from boto.ec2.instance import Reservation +from boto.resultset import ResultSet class ReservationV6(Reservation): diff --git a/doc/source/man/nova-manage.rst b/doc/source/man/nova-manage.rst index 65b9c0d65..4a8169d65 100644 --- a/doc/source/man/nova-manage.rst +++ b/doc/source/man/nova-manage.rst @@ -21,7 +21,7 @@ SYNOPSIS DESCRIPTION =========== -nova-manage controls cloud computing instances by managing nova users, nova projects, nova roles, shell selection, vpn connections, and floating IP address configuration. More information about OpenStack Nova is at http://nova.openstack.org. +nova-manage controls cloud computing instances by managing shell selection, vpn connections, and floating IP address configuration. More information about OpenStack Nova is at http://nova.openstack.org. OPTIONS ======= @@ -29,16 +29,13 @@ OPTIONS The standard pattern for executing a nova-manage command is: ``nova-manage <category> <command> [<args>]`` -For example, to obtain a list of all projects: -``nova-manage project list`` - Run without arguments to see a list of available command categories: ``nova-manage`` -Categories are user, project, role, shell, vpn, and floating. Detailed descriptions are below. +Categories are project, shell, vpn, and floating. Detailed descriptions are below. You can also run with a category argument such as user to see a list of all commands in that category: -``nova-manage user`` +``nova-manage floating`` These sections describe the available categories and arguments for nova-manage. @@ -53,81 +50,6 @@ Nova Db Sync the database up to the most recent version. This is the standard way to create the db as well. -Nova User -~~~~~~~~~ - -``nova-manage user admin <username>`` - - Create an admin user with the name <username>. - -``nova-manage user create <username>`` - - Create a normal user with the name <username>. - -``nova-manage user delete <username>`` - - Delete the user with the name <username>. - -``nova-manage user exports <username>`` - - Outputs a list of access key and secret keys for user to the screen - -``nova-manage user list`` - - Outputs a list of all the user names to the screen. - -``nova-manage user modify <accesskey> <secretkey> <admin?T/F>`` - - Updates the indicated user keys, indicating with T or F if the user is an admin user. Leave any argument blank if you do not want to update it. - -Nova Project -~~~~~~~~~~~~ - -``nova-manage project add <projectname>`` - - Add a nova project with the name <projectname> to the database. - -``nova-manage project create <projectname>`` - - Create a new nova project with the name <projectname> (you still need to do nova-manage project add <projectname> to add it to the database). - -``nova-manage project delete <projectname>`` - - Delete a nova project with the name <projectname>. - -``nova-manage project environment <projectname> <username>`` - - Exports environment variables for the named project to a file named novarc. - -``nova-manage project list`` - - Outputs a list of all the projects to the screen. - -``nova-manage project quota <projectname>`` - - Outputs the size and specs of the project's instances including gigabytes, instances, floating IPs, volumes, and cores. - -``nova-manage project remove <projectname>`` - - Deletes the project with the name <projectname>. - -``nova-manage project zipfile`` - - Compresses all related files for a created project into a zip file nova.zip. - -Nova Role -~~~~~~~~~ - -``nova-manage role add <username> <rolename> <(optional) projectname>`` - - Add a user to either a global or project-based role with the indicated <rolename> assigned to the named user. Role names can be one of the following five roles: cloudadmin, itsec, sysadmin, netadmin, developer. If you add the project name as the last argument then the role is assigned just for that project, otherwise the user is assigned the named role for all projects. - -``nova-manage role has <username> <projectname>`` - Checks the user or project and responds with True if the user has a global role with a particular project. - -``nova-manage role remove <username> <rolename>`` - Remove the indicated role from the user. - Nova Logs ~~~~~~~~~ diff --git a/etc/nova/logging_sample.conf b/etc/nova/logging_sample.conf index 403d70ed9..ff8bc4a21 100644 --- a/etc/nova/logging_sample.conf +++ b/etc/nova/logging_sample.conf @@ -70,7 +70,7 @@ formatter = default args = () [formatter_legacynova] -class = nova.log.LegacyNovaFormatter +class = nova.log.LegacyFormatter [formatter_default] format = %(message)s diff --git a/nova/api/auth.py b/nova/api/auth.py index 72eba1d9c..5191189f2 100644 --- a/nova/api/auth.py +++ b/nova/api/auth.py @@ -18,13 +18,14 @@ Common Auth Middleware. """ +import json import webob.dec import webob.exc from nova import context from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import wsgi @@ -95,13 +96,19 @@ class NovaKeystoneContext(wsgi.Middleware): remote_address = req.remote_addr if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) + + service_catalog = None + if req.headers.get('X_SERVICE_CATALOG') is not None: + service_catalog = json.loads(req.headers.get('X_SERVICE_CATALOG')) + ctx = context.RequestContext(user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=auth_token, - remote_address=remote_address) + remote_address=remote_address, + service_catalog=service_catalog) req.environ['nova.context'] = ctx return self.application diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 6bb19e7b3..9613d0f8b 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -34,10 +34,10 @@ from nova.api import validator from nova import context from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils from nova import wsgi @@ -261,13 +261,16 @@ class EC2KeystoneAuth(wsgi.Middleware): if FLAGS.use_forwarded_for: remote_address = req.headers.get('X-Forwarded-For', remote_address) + + catalog = result['access']['serviceCatalog'] ctxt = context.RequestContext(user_id, project_id, user_name=user_name, project_name=project_name, roles=roles, auth_token=token_id, - remote_address=remote_address) + remote_address=remote_address, + service_catalog=catalog) req.environ['nova.context'] = ctxt diff --git a/nova/api/ec2/apirequest.py b/nova/api/ec2/apirequest.py index c76549562..70b1e3b80 100644 --- a/nova/api/ec2/apirequest.py +++ b/nova/api/ec2/apirequest.py @@ -27,7 +27,7 @@ from xml.dom import minidom from nova.api.ec2 import ec2utils from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) FLAGS = flags.FLAGS diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 126df0f0a..94302383c 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -37,9 +37,9 @@ from nova import db from nova import exception from nova import flags from nova.image import s3 -from nova import log as logging from nova import network from nova.openstack.common import excutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import quota from nova import utils @@ -315,11 +315,13 @@ class CloudController(object): context=context) volume_id = ec2utils.ec2_vol_id_to_uuid(volume_id) volume = self.volume_api.get(context, volume_id) - snapshot = self.volume_api.create_snapshot( - context, - volume, - None, - kwargs.get('description')) + args = (context, volume, kwargs.get('name'), kwargs.get('description')) + if kwargs.get('force', False): + snapshot = self.volume_api.create_snapshot_force(*args) + else: + snapshot = self.volume_api.create_snapshot(*args) + + db.ec2_snapshot_create(context, snapshot['id']) return self._format_snapshot(context, snapshot) def delete_snapshot(self, context, snapshot_id, **kwargs): @@ -723,24 +725,28 @@ class CloudController(object): return v def create_volume(self, context, **kwargs): - size = kwargs.get('size') - if kwargs.get('snapshot_id') is not None: + snapshot_ec2id = kwargs.get('snapshot_id', None) + if snapshot_ec2id is not None: snapshot_id = ec2utils.ec2_snap_id_to_uuid(kwargs['snapshot_id']) snapshot = self.volume_api.get_snapshot(context, snapshot_id) - LOG.audit(_("Create volume from snapshot %s"), snapshot_id, + LOG.audit(_("Create volume from snapshot %s"), snapshot_ec2id, context=context) else: snapshot = None - LOG.audit(_("Create volume of %s GB"), size, context=context) - - availability_zone = kwargs.get('availability_zone', None) + LOG.audit(_("Create volume of %s GB"), + kwargs.get('size'), + context=context) volume = self.volume_api.create(context, - size, - None, - None, + kwargs.get('size'), + kwargs.get('name'), + kwargs.get('description'), snapshot, - availability_zone=availability_zone) + kwargs.get('volume_type'), + kwargs.get('metadata'), + kwargs.get('availability_zone')) + + db.ec2_volume_create(context, volume['id']) # TODO(vish): Instance should be None at db layer instead of # trying to lazy load, but for now we turn it into # a dict to avoid an error. @@ -749,7 +755,6 @@ class CloudController(object): def delete_volume(self, context, volume_id, **kwargs): validate_ec2_id(volume_id) volume_id = ec2utils.ec2_vol_id_to_uuid(volume_id) - try: volume = self.volume_api.get(context, volume_id) self.volume_api.delete(context, volume) @@ -758,7 +763,10 @@ class CloudController(object): return True - def attach_volume(self, context, volume_id, instance_id, device, **kwargs): + def attach_volume(self, context, + volume_id, + instance_id, + device, **kwargs): validate_ec2_id(instance_id) validate_ec2_id(volume_id) volume_id = ec2utils.ec2_vol_id_to_uuid(volume_id) diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py index 608628209..e1ea9c6d8 100644 --- a/nova/api/ec2/ec2utils.py +++ b/nova/api/ec2/ec2utils.py @@ -22,8 +22,8 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.network import model as network_model +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/api/metadata/base.py b/nova/api/metadata/base.py index 582bdec82..ee6e28208 100644 --- a/nova/api/metadata/base.py +++ b/nova/api/metadata/base.py @@ -28,8 +28,8 @@ 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 network +from nova.openstack.common import log as logging from nova import volume FLAGS = flags.FLAGS diff --git a/nova/api/metadata/handler.py b/nova/api/metadata/handler.py index 59a7d030e..d022cbc82 100644 --- a/nova/api/metadata/handler.py +++ b/nova/api/metadata/handler.py @@ -25,7 +25,7 @@ import webob.exc from nova.api.metadata import base from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import wsgi LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 82e958a59..afeed0399 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -25,7 +25,7 @@ import webob.dec import webob.exc from nova.api.openstack import wsgi -from nova import log as logging +from nova.openstack.common import log as logging from nova import wsgi as base_wsgi diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py index 9e9e4d978..078d58639 100644 --- a/nova/api/openstack/auth.py +++ b/nova/api/openstack/auth.py @@ -23,7 +23,7 @@ import webob.exc from nova.api.openstack import wsgi from nova import context from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import wsgi as base_wsgi LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index b8e013f98..88c81a578 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -30,7 +30,7 @@ from nova.compute import utils as compute_utils from nova.compute import vm_states from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import quota diff --git a/nova/api/openstack/compute/__init__.py b/nova/api/openstack/compute/__init__.py index f52234cf1..081290b10 100644 --- a/nova/api/openstack/compute/__init__.py +++ b/nova/api/openstack/compute/__init__.py @@ -32,8 +32,8 @@ from nova.api.openstack.compute import server_metadata from nova.api.openstack.compute import servers from nova.api.openstack.compute import versions from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/__init__.py b/nova/api/openstack/compute/contrib/__init__.py index 4d02ecf7e..d44254eb6 100644 --- a/nova/api/openstack/compute/contrib/__init__.py +++ b/nova/api/openstack/compute/contrib/__init__.py @@ -23,7 +23,7 @@ It can't be called 'extensions' because that causes namespacing problems. from nova.api.openstack import extensions from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/accounts.py b/nova/api/openstack/compute/contrib/accounts.py index 1498ed1c5..e407015c2 100644 --- a/nova/api/openstack/compute/contrib/accounts.py +++ b/nova/api/openstack/compute/contrib/accounts.py @@ -21,7 +21,7 @@ from nova.api.openstack import xmlutil from nova.auth import manager from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 72815ed00..8432f02fc 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -25,7 +25,7 @@ from nova import compute from nova.compute import vm_states from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/aggregates.py b/nova/api/openstack/compute/contrib/aggregates.py index aa10fe532..834131f92 100644 --- a/nova/api/openstack/compute/contrib/aggregates.py +++ b/nova/api/openstack/compute/contrib/aggregates.py @@ -20,7 +20,7 @@ from webob import exc from nova.api.openstack import extensions from nova.compute import api as compute_api from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) authorize = extensions.extension_authorizer('compute', 'aggregates') diff --git a/nova/api/openstack/compute/contrib/certificates.py b/nova/api/openstack/compute/contrib/certificates.py index 554ff10d8..ccc6b84a2 100644 --- a/nova/api/openstack/compute/contrib/certificates.py +++ b/nova/api/openstack/compute/contrib/certificates.py @@ -21,8 +21,8 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil import nova.cert.rpcapi from nova import flags -from nova import log as logging from nova import network +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/cloudpipe.py b/nova/api/openstack/compute/contrib/cloudpipe.py index 321123d76..a2faf833b 100644 --- a/nova/api/openstack/compute/contrib/cloudpipe.py +++ b/nova/api/openstack/compute/contrib/cloudpipe.py @@ -27,8 +27,8 @@ from nova.compute import vm_states from nova import db from nova import exception from nova import flags -from nova import log as logging from nova import network +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils diff --git a/nova/api/openstack/compute/contrib/console_output.py b/nova/api/openstack/compute/contrib/console_output.py index fd1e9418c..7a16daec3 100644 --- a/nova/api/openstack/compute/contrib/console_output.py +++ b/nova/api/openstack/compute/contrib/console_output.py @@ -23,7 +23,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/consoles.py b/nova/api/openstack/compute/contrib/consoles.py index 915ca5c87..ef61d3966 100644 --- a/nova/api/openstack/compute/contrib/consoles.py +++ b/nova/api/openstack/compute/contrib/consoles.py @@ -20,7 +20,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/deferred_delete.py b/nova/api/openstack/compute/contrib/deferred_delete.py index 8ba23a663..8eaea04bb 100644 --- a/nova/api/openstack/compute/contrib/deferred_delete.py +++ b/nova/api/openstack/compute/contrib/deferred_delete.py @@ -22,7 +22,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/extended_server_attributes.py b/nova/api/openstack/compute/contrib/extended_server_attributes.py index 46d4df2d1..f1ec9b956 100644 --- a/nova/api/openstack/compute/contrib/extended_server_attributes.py +++ b/nova/api/openstack/compute/contrib/extended_server_attributes.py @@ -20,7 +20,7 @@ from nova.api.openstack import xmlutil from nova import compute from nova import db from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/extended_status.py b/nova/api/openstack/compute/contrib/extended_status.py index d17319c67..d88f4e14b 100644 --- a/nova/api/openstack/compute/contrib/extended_status.py +++ b/nova/api/openstack/compute/contrib/extended_status.py @@ -19,7 +19,7 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/flavormanage.py b/nova/api/openstack/compute/contrib/flavormanage.py index 547760e94..4dedcf981 100644 --- a/nova/api/openstack/compute/contrib/flavormanage.py +++ b/nova/api/openstack/compute/contrib/flavormanage.py @@ -20,7 +20,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.compute import instance_types from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/floating_ip_dns.py b/nova/api/openstack/compute/contrib/floating_ip_dns.py index a8a7a2a23..788d83b2d 100644 --- a/nova/api/openstack/compute/contrib/floating_ip_dns.py +++ b/nova/api/openstack/compute/contrib/floating_ip_dns.py @@ -22,8 +22,8 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception -from nova import log as logging from nova import network +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/floating_ip_pools.py b/nova/api/openstack/compute/contrib/floating_ip_pools.py index 7265d8611..216bc9091 100644 --- a/nova/api/openstack/compute/contrib/floating_ip_pools.py +++ b/nova/api/openstack/compute/contrib/floating_ip_pools.py @@ -17,8 +17,8 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova import log as logging from nova import network +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/floating_ips.py b/nova/api/openstack/compute/contrib/floating_ips.py index 0b7fa881e..ed5f61d53 100644 --- a/nova/api/openstack/compute/contrib/floating_ips.py +++ b/nova/api/openstack/compute/contrib/floating_ips.py @@ -25,8 +25,8 @@ from nova.api.openstack import xmlutil from nova import compute from nova.compute import utils as compute_utils from nova import exception -from nova import log as logging from nova import network +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/hosts.py b/nova/api/openstack/compute/contrib/hosts.py index e24ef3605..95a80f3f6 100644 --- a/nova/api/openstack/compute/contrib/hosts.py +++ b/nova/api/openstack/compute/contrib/hosts.py @@ -26,7 +26,7 @@ from nova.compute import api as compute_api from nova import db from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/hypervisors.py b/nova/api/openstack/compute/contrib/hypervisors.py index b417319dc..49b050871 100644 --- a/nova/api/openstack/compute/contrib/hypervisors.py +++ b/nova/api/openstack/compute/contrib/hypervisors.py @@ -23,7 +23,7 @@ from nova.api.openstack import xmlutil from nova.compute import api as compute_api from nova import db from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/multinic.py b/nova/api/openstack/compute/contrib/multinic.py index 02e87edf2..29409c050 100644 --- a/nova/api/openstack/compute/contrib/multinic.py +++ b/nova/api/openstack/compute/contrib/multinic.py @@ -22,7 +22,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/networks.py b/nova/api/openstack/compute/contrib/networks.py index 20e481bc7..ece331fbb 100644 --- a/nova/api/openstack/compute/contrib/networks.py +++ b/nova/api/openstack/compute/contrib/networks.py @@ -22,8 +22,8 @@ from webob import exc from nova.api.openstack import extensions from nova import exception from nova import flags -from nova import log as logging import nova.network.api +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/rescue.py b/nova/api/openstack/compute/contrib/rescue.py index 7bf815a37..918f17100 100644 --- a/nova/api/openstack/compute/contrib/rescue.py +++ b/nova/api/openstack/compute/contrib/rescue.py @@ -23,7 +23,7 @@ from nova.api.openstack import wsgi from nova import compute from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/api/openstack/compute/contrib/scheduler_hints.py b/nova/api/openstack/compute/contrib/scheduler_hints.py index 9e0b3ea3a..86b7564bd 100644 --- a/nova/api/openstack/compute/contrib/scheduler_hints.py +++ b/nova/api/openstack/compute/contrib/scheduler_hints.py @@ -18,7 +18,7 @@ import webob.exc from nova.api.openstack import extensions from nova.api.openstack import wsgi -import nova.log as logging +import nova.openstack.common.log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/security_groups.py b/nova/api/openstack/compute/contrib/security_groups.py index 840813d68..004df4e73 100644 --- a/nova/api/openstack/compute/contrib/security_groups.py +++ b/nova/api/openstack/compute/contrib/security_groups.py @@ -29,8 +29,8 @@ from nova import compute from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import excutils +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/api/openstack/compute/contrib/server_start_stop.py b/nova/api/openstack/compute/contrib/server_start_stop.py index ca905d496..049fa393b 100644 --- a/nova/api/openstack/compute/contrib/server_start_stop.py +++ b/nova/api/openstack/compute/contrib/server_start_stop.py @@ -20,7 +20,7 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova import compute from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/users.py b/nova/api/openstack/compute/contrib/users.py index 8eb9b9c5b..12013f014 100644 --- a/nova/api/openstack/compute/contrib/users.py +++ b/nova/api/openstack/compute/contrib/users.py @@ -22,7 +22,7 @@ from nova.api.openstack import xmlutil from nova.auth import manager from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/contrib/virtual_interfaces.py b/nova/api/openstack/compute/contrib/virtual_interfaces.py index e7c0a8735..f4f14dc82 100644 --- a/nova/api/openstack/compute/contrib/virtual_interfaces.py +++ b/nova/api/openstack/compute/contrib/virtual_interfaces.py @@ -20,8 +20,8 @@ from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute -from nova import log as logging from nova import network +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py index a51ba9229..6b04e3240 100644 --- a/nova/api/openstack/compute/contrib/volumes.py +++ b/nova/api/openstack/compute/contrib/volumes.py @@ -26,7 +26,7 @@ from nova.api.openstack import xmlutil from nova import compute from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import volume from nova.volume import volume_types diff --git a/nova/api/openstack/compute/extensions.py b/nova/api/openstack/compute/extensions.py index b52e89ab5..cb5e89deb 100644 --- a/nova/api/openstack/compute/extensions.py +++ b/nova/api/openstack/compute/extensions.py @@ -17,7 +17,7 @@ from nova.api.openstack import extensions as base_extensions from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/images.py b/nova/api/openstack/compute/images.py index 35d21da60..1b20531de 100644 --- a/nova/api/openstack/compute/images.py +++ b/nova/api/openstack/compute/images.py @@ -22,7 +22,7 @@ from nova.api.openstack import xmlutil from nova import exception from nova import flags import nova.image.glance -from nova import log as logging +from nova.openstack.common import log as logging import nova.utils diff --git a/nova/api/openstack/compute/ips.py b/nova/api/openstack/compute/ips.py index 3afb206fa..6ad888fd7 100644 --- a/nova/api/openstack/compute/ips.py +++ b/nova/api/openstack/compute/ips.py @@ -23,7 +23,7 @@ from nova.api.openstack.compute.views import addresses as view_addresses from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 5a8e1f645..1b0ad9463 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -31,7 +31,7 @@ from nova import compute from nova.compute import instance_types from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common.rpc import common as rpc_common from nova.openstack.common import timeutils from nova import utils @@ -524,9 +524,12 @@ class Controller(wsgi.Controller): network_uuid = network['uuid'] if not utils.is_uuid_like(network_uuid): - msg = _("Bad networks format: network uuid is not in" - " proper format (%s)") % network_uuid - raise exc.HTTPBadRequest(explanation=msg) + br_uuid = network_uuid.split('-', 1)[-1] + if not utils.is_uuid_like(br_uuid): + msg = _("Bad networks format: network uuid is " + "not in proper format " + "(%s)") % network_uuid + raise exc.HTTPBadRequest(explanation=msg) #fixed IP address is optional #if the fixed IP address is not provided then diff --git a/nova/api/openstack/compute/views/addresses.py b/nova/api/openstack/compute/views/addresses.py index 794feb5ba..41d1d0730 100644 --- a/nova/api/openstack/compute/views/addresses.py +++ b/nova/api/openstack/compute/views/addresses.py @@ -19,7 +19,7 @@ import itertools from nova.api.openstack import common from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/compute/views/servers.py b/nova/api/openstack/compute/views/servers.py index b0c2fb2b2..9aaf96fe3 100644 --- a/nova/api/openstack/compute/views/servers.py +++ b/nova/api/openstack/compute/views/servers.py @@ -22,7 +22,7 @@ from nova.api.openstack import common from nova.api.openstack.compute.views import addresses as views_addresses from nova.api.openstack.compute.views import flavors as views_flavors from nova.api.openstack.compute.views import images as views_images -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common import timeutils diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py index 93accab93..be7304e6e 100644 --- a/nova/api/openstack/extensions.py +++ b/nova/api/openstack/extensions.py @@ -26,8 +26,8 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import importutils +from nova.openstack.common import log as logging import nova.policy diff --git a/nova/api/openstack/urlmap.py b/nova/api/openstack/urlmap.py index 6f3c484ec..aa49226b3 100644 --- a/nova/api/openstack/urlmap.py +++ b/nova/api/openstack/urlmap.py @@ -20,7 +20,7 @@ import re import urllib2 from nova.api.openstack import wsgi -from nova import log as logging +from nova.openstack.common import log as logging _quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"' diff --git a/nova/api/openstack/volume/__init__.py b/nova/api/openstack/volume/__init__.py index 0f529980f..3e6f731e1 100644 --- a/nova/api/openstack/volume/__init__.py +++ b/nova/api/openstack/volume/__init__.py @@ -26,7 +26,7 @@ from nova.api.openstack.volume import snapshots from nova.api.openstack.volume import types from nova.api.openstack.volume import versions from nova.api.openstack.volume import volumes -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/volume/contrib/__init__.py b/nova/api/openstack/volume/contrib/__init__.py index 1eba0b4df..8e01d88d0 100644 --- a/nova/api/openstack/volume/contrib/__init__.py +++ b/nova/api/openstack/volume/contrib/__init__.py @@ -23,7 +23,7 @@ It can't be called 'extensions' because that causes namespacing problems. from nova.api.openstack import extensions from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/api/openstack/volume/extensions.py b/nova/api/openstack/volume/extensions.py index fa7fc1a77..cf400bd9a 100644 --- a/nova/api/openstack/volume/extensions.py +++ b/nova/api/openstack/volume/extensions.py @@ -17,7 +17,7 @@ from nova.api.openstack import extensions as base_extensions from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/api/openstack/volume/snapshots.py b/nova/api/openstack/volume/snapshots.py index 11681f6e8..209f78d13 100644 --- a/nova/api/openstack/volume/snapshots.py +++ b/nova/api/openstack/volume/snapshots.py @@ -23,7 +23,7 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import volume diff --git a/nova/api/openstack/volume/volumes.py b/nova/api/openstack/volume/volumes.py index 4c50a6d2e..1b0c8dfca 100644 --- a/nova/api/openstack/volume/volumes.py +++ b/nova/api/openstack/volume/volumes.py @@ -23,7 +23,7 @@ from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import volume from nova.volume import volume_types diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index 4f78a4697..fb25c5caa 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -25,8 +25,8 @@ from lxml import etree import webob from nova import exception -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import wsgi diff --git a/nova/api/sizelimit.py b/nova/api/sizelimit.py index a948a1e8a..6c991408d 100644 --- a/nova/api/sizelimit.py +++ b/nova/api/sizelimit.py @@ -22,8 +22,8 @@ import webob.dec import webob.exc from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import wsgi diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 3f36d97b6..ac4fcf4b5 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -29,8 +29,8 @@ import sys from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging ldap_opts = [ diff --git a/nova/auth/manager.py b/nova/auth/manager.py index c4e438daf..8aa3adeab 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -34,9 +34,9 @@ from nova import crypto from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/auth/signer.py b/nova/auth/signer.py index 05be34559..0892aeb9a 100644 --- a/nova/auth/signer.py +++ b/nova/auth/signer.py @@ -59,7 +59,7 @@ except ImportError: boto = None from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/cert/manager.py b/nova/cert/manager.py index 992f5d96b..a7cb6cc12 100644 --- a/nova/cert/manager.py +++ b/nova/cert/manager.py @@ -28,8 +28,8 @@ import base64 from nova import crypto from nova import flags -from nova import log as logging from nova import manager +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) FLAGS = flags.FLAGS diff --git a/nova/cloudpipe/pipelib.py b/nova/cloudpipe/pipelib.py index 0f9f782b0..6926978fc 100644 --- a/nova/cloudpipe/pipelib.py +++ b/nova/cloudpipe/pipelib.py @@ -34,8 +34,8 @@ from nova import crypto from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/common/deprecated.py b/nova/common/deprecated.py index a442c1506..feef86d98 100644 --- a/nova/common/deprecated.py +++ b/nova/common/deprecated.py @@ -16,8 +16,8 @@ from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/compute/api.py b/nova/compute/api.py index 504ed4eec..70528271f 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -40,12 +40,12 @@ from nova.db import base from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova import network from nova import notifications from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils import nova.policy from nova import quota @@ -570,9 +570,8 @@ class API(base.Base): # (--block-device-mapping) if virtual_name == 'NoDevice': values['no_device'] = True - for k in ('delete_on_termination', 'volume_id', - 'snapshot_id', 'volume_id', 'volume_size', - 'virtual_name'): + for k in ('delete_on_termination', 'virtual_name', + 'snapshot_id', 'volume_id', 'volume_size'): values[k] = None self.db.block_device_mapping_update_or_create(elevated_context, @@ -587,7 +586,6 @@ class API(base.Base): instance_uuid = instance['uuid'] mappings = image['properties'].get('mappings', []) if mappings: - instance['shutdown_terminate'] = False self._update_image_block_device_mapping(elevated, instance_type, instance_uuid, mappings) @@ -595,10 +593,17 @@ class API(base.Base): for mapping in (image_bdm, block_device_mapping): if not mapping: continue - instance['shutdown_terminate'] = False self._update_block_device_mapping(elevated, instance_type, instance_uuid, mapping) + def _populate_instance_shutdown_terminate(self, instance, image, + block_device_mapping): + """Populate instance shutdown_terminate information.""" + if (block_device_mapping or + image['properties'].get('mappings') or + image['properties'].get('block_device_mapping')): + instance['shutdown_terminate'] = False + def _populate_instance_names(self, instance): """Populate instance display_name and hostname.""" display_name = instance.get('display_name') @@ -662,11 +667,14 @@ class API(base.Base): self._populate_instance_names(instance) - self._populate_instance_for_bdm(context, instance, - instance_type, image, block_device_mapping) + self._populate_instance_shutdown_terminate(instance, image, + block_device_mapping) instance = self.db.instance_create(context, instance) + self._populate_instance_for_bdm(context, instance, + instance_type, image, block_device_mapping) + # send a state update notification for the initial create to # show it going from non-existent to BUILDING notifications.send_update_with_states(context, instance, None, diff --git a/nova/compute/instance_types.py b/nova/compute/instance_types.py index d06252a6b..dfcf235dd 100644 --- a/nova/compute/instance_types.py +++ b/nova/compute/instance_types.py @@ -26,7 +26,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.openstack.common import log as logging FLAGS = flags.FLAGS LOG = logging.getLogger(__name__) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index e9fbb4e20..a9e9e1562 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -58,7 +58,6 @@ import nova.context from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova import manager from nova import network from nova.network import model as network_model @@ -68,6 +67,7 @@ from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import utils @@ -239,7 +239,7 @@ class ComputeManager(manager.SchedulerDependentManager): try: self.driver = utils.check_isinstance( - importutils.import_object(compute_driver), + importutils.import_object_ns('nova.virt', compute_driver), driver.ComputeDriver) except ImportError as e: LOG.error(_("Unable to load the virtualization driver: %s") % (e)) diff --git a/nova/compute/utils.py b/nova/compute/utils.py index d5905f7e7..81726105c 100644 --- a/nova/compute/utils.py +++ b/nova/compute/utils.py @@ -19,10 +19,11 @@ from nova import db from nova import exception from nova import flags -from nova import log from nova.network import model as network_model from nova import notifications from nova.notifier import api as notifier_api +from nova.openstack.common import log +from nova import utils FLAGS = flags.FLAGS @@ -55,8 +56,13 @@ def notify_usage_exists(context, instance_ref, current_period=False, if system_metadata is None: try: - system_metadata = db.instance_system_metadata_get( - context, instance_ref.uuid) + if instance_ref.get('deleted'): + with utils.temporary_mutation(context, read_deleted='yes'): + system_metadata = db.instance_system_metadata_get( + context, instance_ref.uuid) + else: + system_metadata = db.instance_system_metadata_get( + context, instance_ref.uuid) except exception.NotFound: system_metadata = {} diff --git a/nova/console/manager.py b/nova/console/manager.py index bc5894290..ddb564f06 100644 --- a/nova/console/manager.py +++ b/nova/console/manager.py @@ -22,10 +22,10 @@ import socket from nova.compute import rpcapi as compute_rpcapi from nova import exception from nova import flags -from nova import log as logging from nova import manager from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/console/vmrc_manager.py b/nova/console/vmrc_manager.py index cb6fd51ad..70cc6caee 100644 --- a/nova/console/vmrc_manager.py +++ b/nova/console/vmrc_manager.py @@ -20,10 +20,10 @@ from nova.compute import rpcapi as compute_rpcapi from nova import exception from nova import flags -from nova import log as logging from nova import manager from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.virt import vmwareapi_conn diff --git a/nova/console/xvp.py b/nova/console/xvp.py index 9017c55cd..60197c766 100644 --- a/nova/console/xvp.py +++ b/nova/console/xvp.py @@ -26,8 +26,8 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py index 049ce1fce..c78554c87 100644 --- a/nova/consoleauth/manager.py +++ b/nova/consoleauth/manager.py @@ -21,10 +21,10 @@ import time from nova import flags -from nova import log as logging from nova import manager from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/context.py b/nova/context.py index f2bb6b5d6..5712193fb 100644 --- a/nova/context.py +++ b/nova/context.py @@ -21,8 +21,8 @@ import copy -from nova import log as logging from nova.openstack.common import local +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils @@ -45,7 +45,7 @@ class RequestContext(object): roles=None, remote_address=None, timestamp=None, request_id=None, auth_token=None, overwrite=True, quota_class=None, user_name=None, project_name=None, - **kwargs): + service_catalog=None, **kwargs): """ :param read_deleted: 'no' indicates deleted records are hidden, 'yes' indicates deleted records are visible, 'only' indicates that @@ -80,6 +80,7 @@ class RequestContext(object): request_id = generate_request_id() self.request_id = request_id self.auth_token = auth_token + self.service_catalog = service_catalog # NOTE(markmc): this attribute is currently only used by the # rs_limits turnstile pre-processor. @@ -121,6 +122,7 @@ class RequestContext(object): 'auth_token': self.auth_token, 'quota_class': self.quota_class, 'user_name': self.user_name, + 'service_catalog': self.service_catalog, 'project_name': self.project_name} @classmethod diff --git a/nova/crypto.py b/nova/crypto.py index eb9e36c70..dfab2cd55 100644 --- a/nova/crypto.py +++ b/nova/crypto.py @@ -35,8 +35,8 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 9bf3a0e12..bc82765fb 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -33,7 +33,7 @@ from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils from sqlalchemy import and_ @@ -2900,7 +2900,6 @@ def volume_create(context, values): with session.begin(): volume_ref.save(session=session) - ec2_volume_create(context, volume_ref['id']) return volume_ref @@ -3205,7 +3204,6 @@ def snapshot_create(context, values): session = get_session() with session.begin(): snapshot_ref.save(session=session) - ec2_snapshot_create(context, snapshot_ref['id']) return snapshot_ref diff --git a/nova/db/sqlalchemy/migrate_repo/versions/082_essex.py b/nova/db/sqlalchemy/migrate_repo/versions/082_essex.py index 9a68ee116..4e6048714 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/082_essex.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/082_essex.py @@ -19,7 +19,7 @@ from sqlalchemy import Boolean, BigInteger, Column, DateTime, Float, ForeignKey from sqlalchemy import Index, Integer, MetaData, String, Table, Text from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py b/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py index 2f3b2ecc1..d08afd16e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/083_quota_class.py @@ -17,7 +17,7 @@ from sqlalchemy import Boolean, Column, DateTime from sqlalchemy import MetaData, Integer, String, Table -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/087_add_uuid_to_bw_usage_cache.py b/nova/db/sqlalchemy/migrate_repo/versions/087_add_uuid_to_bw_usage_cache.py index 1c4ac2642..1be008ec3 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/087_add_uuid_to_bw_usage_cache.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/087_add_uuid_to_bw_usage_cache.py @@ -17,7 +17,7 @@ from sqlalchemy import Boolean, Column, DateTime, BigInteger from sqlalchemy import MetaData, Integer, String, Table -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/088_change_instance_id_to_uuid_in_block_device_mapping.py b/nova/db/sqlalchemy/migrate_repo/versions/088_change_instance_id_to_uuid_in_block_device_mapping.py index 46fd7467f..52a340889 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/088_change_instance_id_to_uuid_in_block_device_mapping.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/088_change_instance_id_to_uuid_in_block_device_mapping.py @@ -20,7 +20,7 @@ from migrate import ForeignKeyConstraint from sqlalchemy import MetaData, String, Table from sqlalchemy import select, Column, ForeignKey, Integer -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py b/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py index 120d22707..1dd47aec2 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py @@ -18,7 +18,7 @@ from sqlalchemy import Boolean, Column, DateTime, Integer from sqlalchemy import MetaData, String, Table -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py b/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py index 3366ebc3c..4a2ba2d47 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py @@ -19,7 +19,8 @@ from migrate import ForeignKeyConstraint from sqlalchemy import Integer from sqlalchemy import MetaData, String, Table -from nova import log as logging +from migrate import ForeignKeyConstraint +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py index 00aa95fb8..3c4183f68 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py @@ -17,7 +17,7 @@ from sqlalchemy import MetaData, select, Table -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/092_add_instance_system_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/092_add_instance_system_metadata.py index 2d0e7a0dd..130c4fe59 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/092_add_instance_system_metadata.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/092_add_instance_system_metadata.py @@ -18,7 +18,7 @@ from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer from sqlalchemy import MetaData, String, Table -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py index b561c85c4..08501177d 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py @@ -20,7 +20,7 @@ from migrate import ForeignKeyConstraint from sqlalchemy import MetaData, Integer, String, Table from sqlalchemy import select, Column -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/097_quota_usages_reservations.py b/nova/db/sqlalchemy/migrate_repo/versions/097_quota_usages_reservations.py index f56cc71b9..82d66938c 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/097_quota_usages_reservations.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/097_quota_usages_reservations.py @@ -15,7 +15,7 @@ from sqlalchemy import Boolean, Column, DateTime from sqlalchemy import MetaData, Integer, String, Table, ForeignKey -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/099_add_disabled_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/099_add_disabled_instance_types.py index 166ca98cb..549426608 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/099_add_disabled_instance_types.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/099_add_disabled_instance_types.py @@ -14,7 +14,7 @@ from sqlalchemy import Boolean, Column, MetaData, Table -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/100_instance_metadata_uses_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/100_instance_metadata_uses_uuid.py index 4c02ef557..e5c2a275d 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/100_instance_metadata_uses_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/100_instance_metadata_uses_uuid.py @@ -20,7 +20,7 @@ from migrate import ForeignKeyConstraint from sqlalchemy import MetaData, String, Table from sqlalchemy import select, Column, ForeignKey, Integer -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/101_security_group_instance_association_uses_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/101_security_group_instance_association_uses_uuid.py index 42aba6b7b..26b53bb7e 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/101_security_group_instance_association_uses_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/101_security_group_instance_association_uses_uuid.py @@ -20,7 +20,7 @@ from migrate import ForeignKeyConstraint from sqlalchemy import MetaData, String, Table from sqlalchemy import select, Column, ForeignKey, Integer -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/102_consoles_uses_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/102_consoles_uses_uuid.py index 7484b73c6..1cfa523c6 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/102_consoles_uses_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/102_consoles_uses_uuid.py @@ -20,7 +20,7 @@ from migrate import ForeignKeyConstraint from sqlalchemy import MetaData, String, Table from sqlalchemy import select, Column, ForeignKey, Integer -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/105_instance_info_caches_uses_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/105_instance_info_caches_uses_uuid.py index f98f399c8..c1c7d7a44 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/105_instance_info_caches_uses_uuid.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/105_instance_info_caches_uses_uuid.py @@ -20,7 +20,7 @@ from sqlalchemy import select, Column, ForeignKey, Integer from sqlalchemy import MetaData, String, Table from migrate import ForeignKeyConstraint -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/106_add_foreign_keys.py b/nova/db/sqlalchemy/migrate_repo/versions/106_add_foreign_keys.py index 1aed8588e..8d867609c 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/106_add_foreign_keys.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/106_add_foreign_keys.py @@ -20,7 +20,7 @@ from sqlalchemy import select, Column, ForeignKey, Integer from sqlalchemy import MetaData, String, Table from migrate import ForeignKeyConstraint -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py b/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py index 94adbd89b..39985a1af 100644 --- a/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py +++ b/nova/db/sqlalchemy/migrate_repo/versions/107_add_instance_id_mappings.py @@ -14,9 +14,9 @@ # License for the specific language governing permissions and limitations # under the License. +from nova.openstack.common import log as logging from sqlalchemy import Boolean, Column, DateTime, Integer from sqlalchemy import Index, MetaData, String, Table -from nova import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/db/sqlalchemy/migration.py b/nova/db/sqlalchemy/migration.py index e7c659a70..69375d05c 100644 --- a/nova/db/sqlalchemy/migration.py +++ b/nova/db/sqlalchemy/migration.py @@ -23,7 +23,7 @@ from nova.db import migration from nova.db.sqlalchemy.session import get_engine from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging import migrate diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py index 4f115e16c..a161c0327 100644 --- a/nova/db/sqlalchemy/session.py +++ b/nova/db/sqlalchemy/session.py @@ -27,7 +27,7 @@ from sqlalchemy.pool import NullPool, StaticPool import nova.exception import nova.flags as flags -import nova.log as logging +import nova.openstack.common.log as logging FLAGS = flags.FLAGS diff --git a/nova/exception.py b/nova/exception.py index df21f0273..c1f417afe 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -29,8 +29,8 @@ import itertools import webob.exc -from nova import log as logging from nova.openstack.common import excutils +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/flags.py b/nova/flags.py index bb769c4dd..cff3b46ae 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -73,18 +73,6 @@ def _get_my_ip(): return "127.0.0.1" -log_opts = [ - cfg.StrOpt('logdir', - default=None, - help='Log output to a per-service log file in named directory'), - cfg.StrOpt('logfile', - default=None, - help='Log output to a named file'), - cfg.BoolOpt('use_stderr', - default=True, - help='Log output to standard error'), - ] - core_opts = [ cfg.StrOpt('connection_type', default=None, @@ -125,7 +113,6 @@ debug_opts = [ help='Add python stack traces to SQL as comment strings'), ] -FLAGS.register_cli_opts(log_opts) FLAGS.register_cli_opts(core_opts) FLAGS.register_cli_opts(debug_opts) @@ -273,9 +260,6 @@ global_opts = [ cfg.IntOpt('auth_token_ttl', default=3600, help='Seconds for auth tokens to linger'), - cfg.StrOpt('logfile_mode', - default='0644', - help='Default file mode used when creating log files'), cfg.StrOpt('sqlite_db', default='nova.sqlite', help='the filename to use with sqlite'), diff --git a/nova/image/glance.py b/nova/image/glance.py index d8c2e1245..2da91c9b6 100644 --- a/nova/image/glance.py +++ b/nova/image/glance.py @@ -30,8 +30,8 @@ from glance.common import exception as glance_exception from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils diff --git a/nova/image/s3.py b/nova/image/s3.py index df2758b3a..282f7702a 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -34,8 +34,8 @@ import nova.cert.rpcapi from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/manager.py b/nova/manager.py index 1d73040b4..a74caa8f6 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -55,7 +55,7 @@ This module provides Manager, a base class for managers. from nova.db import base from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common.rpc import dispatcher as rpc_dispatcher from nova.scheduler import rpcapi as scheduler_rpcapi from nova import version diff --git a/nova/network/api.py b/nova/network/api.py index a36edd796..6a5c151da 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -22,8 +22,8 @@ import inspect from nova.db import base from nova import flags -from nova import log as logging from nova.network import model as network_model +from nova.openstack.common import log as logging from nova.openstack.common import rpc diff --git a/nova/network/l3.py b/nova/network/l3.py index 034678aa5..e098c1e8f 100644 --- a/nova/network/l3.py +++ b/nova/network/l3.py @@ -16,8 +16,8 @@ # under the License. from nova import flags -from nova import log as logging from nova.network import linux_net +from nova.openstack.common import log as logging from nova import utils LOG = logging.getLogger(__name__) diff --git a/nova/network/ldapdns.py b/nova/network/ldapdns.py index 25e6e55ad..e3ba3006c 100644 --- a/nova/network/ldapdns.py +++ b/nova/network/ldapdns.py @@ -18,8 +18,8 @@ import time from nova.auth import fakeldap from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 828174b3a..d5c6bb459 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -27,9 +27,9 @@ import os from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/network/manager.py b/nova/network/manager.py index 402513faf..d4937d90b 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -58,7 +58,6 @@ from nova import context from nova import exception from nova import flags from nova import ipv6 -from nova import log as logging from nova import manager from nova.network import api as network_api from nova.network import model as network_model @@ -67,6 +66,7 @@ from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common import timeutils import nova.policy @@ -987,8 +987,7 @@ class NetworkManager(manager.SchedulerDependentManager): context=read_deleted_context) # deallocate fixed ips for fixed_ip in fixed_ips: - self.deallocate_fixed_ip(read_deleted_context, fixed_ip['address'], - **kwargs) + self.deallocate_fixed_ip(context, fixed_ip['address'], **kwargs) # deallocate vifs (mac addresses) self.db.virtual_interface_delete_by_instance(read_deleted_context, diff --git a/nova/network/quantum/manager.py b/nova/network/quantum/manager.py index 95ee06c9f..9fa852c09 100644 --- a/nova/network/quantum/manager.py +++ b/nova/network/quantum/manager.py @@ -23,11 +23,11 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.network import manager from nova.network.quantum import melange_ipam_lib from nova.network.quantum import quantum_connection from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import utils diff --git a/nova/network/quantum/melange_connection.py b/nova/network/quantum/melange_connection.py index dd16afc71..342222909 100644 --- a/nova/network/quantum/melange_connection.py +++ b/nova/network/quantum/melange_connection.py @@ -22,9 +22,9 @@ import urllib from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging melange_opts = [ diff --git a/nova/network/quantum/melange_ipam_lib.py b/nova/network/quantum/melange_ipam_lib.py index 6ceabc8a4..44f4b3c69 100644 --- a/nova/network/quantum/melange_ipam_lib.py +++ b/nova/network/quantum/melange_ipam_lib.py @@ -20,8 +20,8 @@ import netaddr from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.network.quantum import melange_connection +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/network/quantum/nova_ipam_lib.py b/nova/network/quantum/nova_ipam_lib.py index ceb589562..08e19f0a4 100644 --- a/nova/network/quantum/nova_ipam_lib.py +++ b/nova/network/quantum/nova_ipam_lib.py @@ -21,8 +21,8 @@ from nova import db from nova import exception from nova import flags from nova import ipv6 -from nova import log as logging from nova.network import manager +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/network/quantum/quantum_connection.py b/nova/network/quantum/quantum_connection.py index 52a9fc301..2161c7916 100644 --- a/nova/network/quantum/quantum_connection.py +++ b/nova/network/quantum/quantum_connection.py @@ -16,9 +16,9 @@ # under the License. from nova import flags -from nova import log as logging from nova.network.quantum import client as quantum_client from nova.openstack.common import cfg +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/network/quantum/sg.py b/nova/network/quantum/sg.py index 7212280c5..7f41a9983 100644 --- a/nova/network/quantum/sg.py +++ b/nova/network/quantum/sg.py @@ -27,7 +27,7 @@ will provide enhanced functionality and will be loadable via the security_group_handler flag. ''' -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/network/quantumv2/__init__.py b/nova/network/quantumv2/__init__.py new file mode 100644 index 000000000..af114a80c --- /dev/null +++ b/nova/network/quantumv2/__init__.py @@ -0,0 +1,58 @@ +# Copyright 2012 OpenStack LLC. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from nova import exception +from nova import flags +from nova.openstack.common import excutils +from nova.openstack.common import log as logging +from quantumclient import client +from quantumclient.v2_0 import client as clientv20 + +FLAGS = flags.FLAGS +LOG = logging.getLogger(__name__) + + +def _get_auth_token(): + try: + httpclient = client.HTTPClient( + username=FLAGS.quantum_admin_username, + tenant_name=FLAGS.quantum_admin_tenant_name, + password=FLAGS.quantum_admin_password, + auth_url=FLAGS.quantum_admin_auth_url, + timeout=FLAGS.quantum_url_timeout, + auth_strategy=FLAGS.quantum_auth_strategy) + httpclient.authenticate() + except Exception: + with excutils.save_and_reraise_exception(): + LOG.exception(_("_get_auth_token() failed")) + return httpclient.auth_token + + +def get_client(context): + token = context.auth_token + if not token: + if FLAGS.quantum_auth_strategy: + token = _get_auth_token() + if token: + my_client = clientv20.Client( + endpoint_url=FLAGS.quantum_url, + token=token, timeout=FLAGS.quantum_url_timeout) + else: + my_client = clientv20.Client( + endpoint_url=FLAGS.quantum_url, + auth_strategy=None, timeout=FLAGS.quantum_url_timeout) + return my_client diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py new file mode 100644 index 000000000..1cac12f8e --- /dev/null +++ b/nova/network/quantumv2/api.py @@ -0,0 +1,344 @@ +# Copyright 2012 OpenStack LLC. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from nova.db import base +from nova import exception +from nova import flags +from nova.network.api import refresh_cache +from nova.network import model as network_model +from nova.network import quantumv2 +from nova.openstack.common import cfg +from nova.openstack.common import excutils +from nova.openstack.common import log as logging + +quantum_opts = [ + cfg.StrOpt('quantum_url', + default='http://127.0.0.1:9696', + help='URL for connecting to quantum'), + cfg.IntOpt('quantum_url_timeout', + default=30, + help='timeout value for connecting to quantum in seconds'), + cfg.StrOpt('quantum_admin_username', + help='username for connecting to quantum in admin context'), + cfg.StrOpt('quantum_admin_password', + help='password for connecting to quantum in admin context'), + cfg.StrOpt('quantum_admin_tenant_name', + help='tenant name for connecting to quantum in admin context'), + cfg.StrOpt('quantum_admin_auth_url', + default='http://localhost:5000/v2.0', + help='auth url for connecting to quantum in admin context'), + cfg.StrOpt('quantum_auth_strategy', + default='keystone', + help='auth strategy for connecting to ' + 'quantum in admin context'), + ] + +FLAGS = flags.FLAGS +FLAGS.register_opts(quantum_opts) + +LOG = logging.getLogger(__name__) + + +class API(base.Base): + """API for interacting with the quantum 2.x API.""" + + def setup_networks_on_host(self, context, instance, host=None, + teardown=False): + """Setup or teardown the network structures.""" + + def allocate_for_instance(self, context, instance, **kwargs): + """Allocate all network resources for the instance.""" + LOG.debug(_('allocate_for_instance() for %s'), + instance['display_name']) + search_opts = {} + if instance['project_id']: + search_opts.update({"tenant_id": instance['project_id']}) + else: + msg = _('empty project id for instance %s') + raise exception.InvalidInput( + reason=msg % instance['display_name']) + data = quantumv2.get_client(context).list_networks(**search_opts) + nets = data.get('networks', []) + created_port_ids = [] + for network in nets: + port_req_body = {'port': {'network_id': network['id'], + 'admin_state_up': True, + 'device_id': instance['uuid'], + 'tenant_id': instance['project_id']}, + } + try: + created_port_ids.append( + quantumv2.get_client(context).create_port( + port_req_body)['port']['id']) + except Exception: + with excutils.save_and_reraise_exception(): + for port_id in created_port_ids: + try: + quantumv2.get_client(context).delete_port(port_id) + except Exception as ex: + msg = _("Fail to delete port %(portid)s with" + " failure: %(exception)s") + LOG.debug(msg, {'portid': port_id, + 'exception': ex}) + return self.get_instance_nw_info(context, instance, networks=nets) + + def deallocate_for_instance(self, context, instance, **kwargs): + """Deallocate all network resources related to the instance.""" + LOG.debug(_('deallocate_for_instance() for %s'), + instance['display_name']) + search_opts = {'device_id': instance['uuid']} + data = quantumv2.get_client(context).list_ports(**search_opts) + ports = data.get('ports', []) + for port in ports: + try: + quantumv2.get_client(context).delete_port(port['id']) + except Exception as ex: + with excutils.save_and_reraise_exception(): + msg = _("Fail to delete port %(portid)s with failure:" + "%(exception)s") + LOG.debug(msg, {'portid': port['id'], + 'exception': ex}) + + @refresh_cache + def get_instance_nw_info(self, context, instance, networks=None): + LOG.debug(_('get_instance_nw_info() for %s'), + instance['display_name']) + nw_info = self._build_network_info_model(context, instance, networks) + return network_model.NetworkInfo.hydrate(nw_info) + + def add_fixed_ip_to_instance(self, context, instance, network_id): + """Add a fixed ip to the instance from specified network.""" + raise NotImplemented() + + def remove_fixed_ip_from_instance(self, context, instance, address): + """Remove a fixed ip from the instance.""" + raise NotImplemented() + + def validate_networks(self, context, requested_networks): + """Validate that the tenant has the requested networks.""" + LOG.debug(_('validate_networks() for %s'), + requested_networks) + if not requested_networks: + return + search_opts = {"tenant_id": context.project_id} + net_ids = [net_id for (net_id, _i) in requested_networks] + search_opts['id'] = net_ids + data = quantumv2.get_client(context).list_networks(**search_opts) + nets = data.get('networks', []) + if len(nets) != len(net_ids): + requsted_netid_set = set(net_ids) + returned_netid_set = set([net['id'] for net in nets]) + lostid_set = requsted_netid_set - returned_netid_set + id_str = '' + for _id in lostid_set: + id_str = id_str and id_str + ', ' + _id or _id + raise exception.NetworkNotFound(network_id=id_str) + + def get_instance_uuids_by_ip_filter(self, context, filters): + """Return a list of dicts in the form of + [{'instance_uuid': uuid}] that matched the ip filter. + """ + # filters['ip'] is composed as '^%s$' % fixed_ip.replace('.', '\\.') + ip = filters.get('ip') + # we remove ^$\ in the ip filer + if ip[0] == '^': + ip = ip[1:] + if ip[-1] == '$': + ip = ip[:-1] + ip = ip.replace('\\.', '.') + search_opts = {"fixed_ips": {'ip_address': ip}} + data = quantumv2.get_client(context).list_ports(**search_opts) + ports = data.get('ports', []) + + return [{'instance_uuid': port['device_id']} for port in ports + if port['device_id']] + + @refresh_cache + def associate_floating_ip(self, context, instance, + floating_address, fixed_address, + affect_auto_assigned=False): + """Associate a floating ip with a fixed ip.""" + raise NotImplemented() + + def get_all(self, context): + raise NotImplemented() + + def get(self, context, network_uuid): + raise NotImplemented() + + def delete(self, context, network_uuid): + raise NotImplemented() + + def disassociate(self, context, network_uuid): + raise NotImplemented() + + def get_fixed_ip(self, context, id): + raise NotImplemented() + + def get_fixed_ip_by_address(self, context, address): + raise NotImplemented() + + def get_floating_ip(self, context, id): + raise NotImplemented() + + def get_floating_ip_pools(self, context): + raise NotImplemented() + + def get_floating_ip_by_address(self, context, address): + raise NotImplemented() + + def get_floating_ips_by_project(self, context): + raise NotImplemented() + + def get_floating_ips_by_fixed_address(self, context, fixed_address): + raise NotImplemented() + + def get_instance_id_by_floating_address(self, context, address): + raise NotImplemented() + + def get_vifs_by_instance(self, context, instance): + raise NotImplemented() + + def get_vif_by_mac_address(self, context, mac_address): + raise NotImplemented() + + def allocate_floating_ip(self, context, pool=None): + """Add a floating ip to a project from a pool.""" + raise NotImplemented() + + def release_floating_ip(self, context, address, + affect_auto_assigned=False): + """Remove a floating ip with the given address from a project.""" + raise NotImplemented() + + @refresh_cache + def disassociate_floating_ip(self, context, instance, address, + affect_auto_assigned=False): + """Disassociate a floating ip from the fixed ip + it is associated with.""" + raise NotImplemented() + + def add_network_to_project(self, context, project_id): + """Force add a network to the project.""" + raise NotImplemented() + + def _build_network_info_model(self, context, instance, networks=None): + search_opts = {'tenant_id': instance['project_id'], + 'device_id': instance['uuid'], } + data = quantumv2.get_client(context).list_ports(**search_opts) + ports = data.get('ports', []) + if not networks: + search_opts = {} + if instance['project_id']: + search_opts.update({"tenant_id": instance['project_id']}) + data = quantumv2.get_client(context).list_networks(**search_opts) + networks = data.get('networks', []) + nw_info = network_model.NetworkInfo() + for port in ports: + network_name = None + for net in networks: + if port['network_id'] == net['id']: + network_name = net['name'] + break + + subnets = self._get_subnets_from_port(context, port) + network_IPs = [network_model.FixedIP(address=ip_address) + for ip_address in [ip['ip_address'] + for ip in port['fixed_ips']]] + # TODO(gongysh) get floating_ips for each fixed_ip + + for subnet in subnets: + subnet['ips'] = [fixed_ip for fixed_ip in network_IPs + if fixed_ip.is_in_subnet(subnet)] + + network = network_model.Network( + id=port['network_id'], + bridge='', # Quantum ignores this field + injected=FLAGS.flat_injected, + label=network_name, + tenant_id=net['tenant_id'] + ) + network['subnets'] = subnets + nw_info.append(network_model.VIF( + id=port['id'], + address=port['mac_address'], + network=network)) + return nw_info + + def _get_subnets_from_port(self, context, port): + """Return the subnets for a given port.""" + + fixed_ips = port['fixed_ips'] + search_opts = {'id': [ip['subnet_id'] for ip in fixed_ips]} + data = quantumv2.get_client(context).list_subnets(**search_opts) + ipam_subnets = data.get('subnets', []) + subnets = [] + for subnet in ipam_subnets: + subnet_dict = {'cidr': subnet['cidr'], + 'gateway': network_model.IP( + address=subnet['gateway_ip'], + type='gateway'), + } + # TODO(gongysh) deal with dhcp + + subnet_object = network_model.Subnet(**subnet_dict) + for dns in subnet.get('dns_nameservers', []): + subnet_object.add_dns( + network_model.IP(address=dns, type='dns')) + + # TODO(gongysh) get the routes for this subnet + subnets.append(subnet_object) + return subnets + + def get_dns_domains(self, context): + """Return a list of available dns domains. + + These can be used to create DNS entries for floating ips. + """ + raise NotImplemented() + + def add_dns_entry(self, context, address, name, dns_type, domain): + """Create specified DNS entry for address.""" + raise NotImplemented() + + def modify_dns_entry(self, context, name, address, domain): + """Create specified DNS entry for address.""" + raise NotImplemented() + + def delete_dns_entry(self, context, name, domain): + """Delete the specified dns entry.""" + raise NotImplemented() + + def delete_dns_domain(self, context, domain): + """Delete the specified dns domain.""" + raise NotImplemented() + + def get_dns_entries_by_address(self, context, address, domain): + """Get entries for address and domain.""" + raise NotImplemented() + + def get_dns_entries_by_name(self, context, name, domain): + """Get entries for name and domain.""" + raise NotImplemented() + + def create_private_dns_domain(self, context, domain, availability_zone): + """Create a private DNS domain with nova availability zone.""" + raise NotImplemented() + + def create_public_dns_domain(self, context, domain, project=None): + """Create a private DNS domain with optional nova project.""" + raise NotImplemented() diff --git a/nova/notifications.py b/nova/notifications.py index 29bca2c2f..933af1f20 100644 --- a/nova/notifications.py +++ b/nova/notifications.py @@ -23,11 +23,11 @@ import nova.context from nova import db from nova import exception from nova import flags -from nova import log from nova import network from nova.network import model as network_model from nova.notifier import api as notifier_api from nova.openstack.common import cfg +from nova.openstack.common import log from nova.openstack.common import timeutils from nova import utils @@ -109,16 +109,6 @@ def _send_instance_update_notification(context, instance, old_vm_state, bw = bandwidth_usage(instance, audit_start) payload["bandwidth"] = bw - try: - system_metadata = db.instance_system_metadata_get( - context, instance.uuid) - except exception.NotFound: - system_metadata = {} - - # add image metadata - image_meta_props = image_meta(system_metadata) - payload["image_meta"] = image_meta_props - # if the service name (e.g. api/scheduler/compute) is not provided, default # to "compute" if not service: @@ -222,6 +212,19 @@ def usage_from_instance(context, instance_ref, network_info, instance_type_name = instance_ref.get('instance_type', {}).get('name', '') + if system_metadata is None: + try: + if instance_ref.get('deleted'): + with utils.temporary_mutation(context, read_deleted='yes'): + system_metadata = db.instance_system_metadata_get( + context, instance_ref['uuid']) + else: + system_metadata = db.instance_system_metadata_get( + context, instance_ref['uuid']) + + except exception.NotFound: + system_metadata = {} + usage_info = dict( # Owner properties tenant_id=instance_ref['project_id'], @@ -273,5 +276,9 @@ def usage_from_instance(context, instance_ref, network_info, if network_info is not None: usage_info['fixed_ips'] = network_info.fixed_ips() + # add image metadata + image_meta_props = image_meta(system_metadata) + usage_info["image_meta"] = image_meta_props + usage_info.update(kw) return usage_info diff --git a/nova/notifier/api.py b/nova/notifier/api.py index cf96dfed4..df3b39a68 100644 --- a/nova/notifier/api.py +++ b/nova/notifier/api.py @@ -17,10 +17,10 @@ import uuid from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils diff --git a/nova/notifier/capacity_notifier.py b/nova/notifier/capacity_notifier.py index b8e3a88e2..ea78382d8 100644 --- a/nova/notifier/capacity_notifier.py +++ b/nova/notifier/capacity_notifier.py @@ -15,7 +15,7 @@ from nova import context from nova import db -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/notifier/list_notifier.py b/nova/notifier/list_notifier.py index 4e73bdd50..5aa7738b2 100644 --- a/nova/notifier/list_notifier.py +++ b/nova/notifier/list_notifier.py @@ -14,9 +14,9 @@ # under the License. from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging list_notifier_drivers_opt = cfg.MultiStrOpt('list_notifier_drivers', diff --git a/nova/notifier/log_notifier.py b/nova/notifier/log_notifier.py index 50528d3e6..5ce6dc4af 100644 --- a/nova/notifier/log_notifier.py +++ b/nova/notifier/log_notifier.py @@ -14,8 +14,8 @@ # under the License. from nova import flags -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/notifier/rabbit_notifier.py b/nova/notifier/rabbit_notifier.py index 27f6ea209..867ad9c19 100644 --- a/nova/notifier/rabbit_notifier.py +++ b/nova/notifier/rabbit_notifier.py @@ -17,8 +17,8 @@ import nova.context from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import rpc LOG = logging.getLogger(__name__) diff --git a/nova/openstack/common/cfg.py b/nova/openstack/common/cfg.py index dd367aeb6..6d71b0304 100644 --- a/nova/openstack/common/cfg.py +++ b/nova/openstack/common/cfg.py @@ -391,7 +391,7 @@ def _get_config_dirs(project=None): fix_path('~'), os.path.join('/etc', project) if project else None, '/etc' - ] + ] return filter(bool, cfg_dirs) @@ -489,12 +489,13 @@ class Opt(object): metavar: the name shown as the argument to a CLI option in --help output help: - a string explaining how the options value is used + an string explaining how the options value is used """ multi = False def __init__(self, name, dest=None, short=None, default=None, - metavar=None, help=None, secret=False, required=False): + metavar=None, help=None, secret=False, required=False, + deprecated_name=None): """Construct an Opt object. The only required parameter is the option's name. However, it is @@ -508,6 +509,7 @@ class Opt(object): :param help: an explanation of how the option is used :param secret: true iff the value should be obfuscated in log output :param required: true iff a value must be supplied for this option + :param deprecated_name: deprecated name option. Acts like an alias """ self.name = name if dest is None: @@ -520,6 +522,10 @@ class Opt(object): self.help = help self.secret = secret self.required = required + if deprecated_name is not None: + self.deprecated_name = deprecated_name.replace('-', '_') + else: + self.deprecated_name = None def _get_from_config_parser(self, cparser, section): """Retrieves the option value from a MultiConfigParser object. @@ -531,7 +537,13 @@ class Opt(object): :param cparser: a ConfigParser object :param section: a section name """ - return cparser.get(section, self.dest) + return self._cparser_get_with_deprecated(cparser, section) + + def _cparser_get_with_deprecated(self, cparser, section): + """If cannot find option as dest try deprecated_name alias.""" + if self.deprecated_name is not None: + return cparser.get(section, [self.dest, self.deprecated_name]) + return cparser.get(section, [self.dest]) def _add_to_cli(self, parser, group=None): """Makes the option available in the command line interface. @@ -546,9 +558,11 @@ class Opt(object): container = self._get_optparse_container(parser, group) kwargs = self._get_optparse_kwargs(group) prefix = self._get_optparse_prefix('', group) - self._add_to_optparse(container, self.name, self.short, kwargs, prefix) + self._add_to_optparse(container, self.name, self.short, kwargs, prefix, + self.deprecated_name) - def _add_to_optparse(self, container, name, short, kwargs, prefix=''): + def _add_to_optparse(self, container, name, short, kwargs, prefix='', + deprecated_name=None): """Add an option to an optparse parser or group. :param container: an optparse.OptionContainer object @@ -561,6 +575,8 @@ class Opt(object): args = ['--' + prefix + name] if short: args += ['-' + short] + if deprecated_name: + args += ['--' + prefix + deprecated_name] for a in args: if container.has_option(a): raise DuplicateOptError(a) @@ -591,11 +607,9 @@ class Opt(object): dest = self.dest if group is not None: dest = group.name + '_' + dest - kwargs.update({ - 'dest': dest, - 'metavar': self.metavar, - 'help': self.help, - }) + kwargs.update({'dest': dest, + 'metavar': self.metavar, + 'help': self.help, }) return kwargs def _get_optparse_prefix(self, prefix, group): @@ -645,7 +659,8 @@ class BoolOpt(Opt): return value - return [convert_bool(v) for v in cparser.get(section, self.dest)] + return [convert_bool(v) for v in + self._cparser_get_with_deprecated(cparser, section)] def _add_to_cli(self, parser, group=None): """Extends the base class method to add the --nooptname option.""" @@ -658,7 +673,8 @@ class BoolOpt(Opt): kwargs = self._get_optparse_kwargs(group, action='store_false') prefix = self._get_optparse_prefix('no', group) kwargs["help"] = "The inverse of --" + self.name - self._add_to_optparse(container, self.name, None, kwargs, prefix) + self._add_to_optparse(container, self.name, None, kwargs, prefix, + self.deprecated_name) def _get_optparse_kwargs(self, group, action='store_true', **kwargs): """Extends the base optparse keyword dict for boolean options.""" @@ -671,8 +687,9 @@ class IntOpt(Opt): """Int opt values are converted to integers using the int() builtin.""" def _get_from_config_parser(self, cparser, section): - """Retrieve the opt value as an integer from ConfigParser.""" - return [int(v) for v in cparser.get(section, self.dest)] + """Retrieve the opt value as a integer from ConfigParser.""" + return [int(v) for v in self._cparser_get_with_deprecated(cparser, + section)] def _get_optparse_kwargs(self, group, **kwargs): """Extends the base optparse keyword dict for integer options.""" @@ -686,7 +703,8 @@ class FloatOpt(Opt): def _get_from_config_parser(self, cparser, section): """Retrieve the opt value as a float from ConfigParser.""" - return [float(v) for v in cparser.get(section, self.dest)] + return [float(v) for v in + self._cparser_get_with_deprecated(cparser, section)] def _get_optparse_kwargs(self, group, **kwargs): """Extends the base optparse keyword dict for float options.""" @@ -703,7 +721,8 @@ class ListOpt(Opt): def _get_from_config_parser(self, cparser, section): """Retrieve the opt value as a list from ConfigParser.""" - return [v.split(',') for v in cparser.get(section, self.dest)] + return [v.split(',') for v in + self._cparser_get_with_deprecated(cparser, section)] def _get_optparse_kwargs(self, group, **kwargs): """Extends the base optparse keyword dict for list options.""" @@ -732,6 +751,13 @@ class MultiStrOpt(Opt): return super(MultiStrOpt, self)._get_optparse_kwargs(group, action='append') + def _cparser_get_with_deprecated(self, cparser, section): + """If cannot find option as dest try deprecated_name alias.""" + if self.deprecated_name is not None: + return cparser.get(section, [self.dest, self.deprecated_name], + multi=True) + return cparser.get(section, [self.dest], multi=True) + class OptGroup(object): @@ -846,25 +872,38 @@ class ConfigParser(iniparser.BaseParser): class MultiConfigParser(object): def __init__(self): - self.sections = {} + self.parsed = [] def read(self, config_files): read_ok = [] for filename in config_files: - parser = ConfigParser(filename, self.sections) + sections = {} + parser = ConfigParser(filename, sections) try: parser.parse() except IOError: continue - + self.parsed.insert(0, sections) read_ok.append(filename) return read_ok - def get(self, section, name): - return self.sections[section][name] + def get(self, section, names, multi=False): + rvalue = [] + for sections in self.parsed: + if section not in sections: + continue + for name in names: + if name in sections[section]: + if multi: + rvalue = sections[section][name] + rvalue + else: + return sections[section][name] + if multi and rvalue != []: + return rvalue + raise KeyError class ConfigOpts(collections.Mapping): @@ -905,13 +944,13 @@ class ConfigOpts(collections.Mapping): self._oparser.disable_interspersed_args() self._config_opts = [ - MultiStrOpt('config-file', - default=default_config_files, - metavar='PATH', - help='Path to a config file to use. Multiple config ' - 'files can be specified, with values in later ' - 'files taking precedence. The default files ' - ' used are: %s' % (default_config_files, )), + MultiStrOpt('config-file', + default=default_config_files, + metavar='PATH', + help='Path to a config file to use. Multiple config ' + 'files can be specified, with values in later ' + 'files taking precedence. The default files ' + ' used are: %s' % (default_config_files, )), StrOpt('config-dir', metavar='DIR', help='Path to a config directory to pull *.conf ' @@ -921,7 +960,7 @@ class ConfigOpts(collections.Mapping): 'the file(s), if any, specified via --config-file, ' 'hence over-ridden options in the directory take ' 'precedence.'), - ] + ] self.register_cli_opts(self._config_opts) self.project = project @@ -1323,7 +1362,7 @@ class ConfigOpts(collections.Mapping): def _substitute(self, value): """Perform string template substitution. - Substititue any template variables (e.g. $foo, ${bar}) in the supplied + Substitute any template variables (e.g. $foo, ${bar}) in the supplied string value(s) with opt values. :param value: the string value, or list of string values @@ -1338,7 +1377,7 @@ class ConfigOpts(collections.Mapping): return value def _get_group(self, group_or_name, autocreate=False): - """Looks up an OptGroup object. + """Looks up a OptGroup object. Helper function to return an OptGroup given a parameter which can either be the group's name or an OptGroup object. @@ -1411,8 +1450,7 @@ class ConfigOpts(collections.Mapping): default, opt, override = [info[k] for k in sorted(info.keys())] if opt.required: - if (default is not None or - override is not None): + if (default is not None or override is not None): continue if self._get(opt.name, group) is None: @@ -1516,7 +1554,7 @@ class CommonConfigOpts(ConfigOpts): short='v', default=False, help='Print more verbose output'), - ] + ] logging_cli_opts = [ StrOpt('log-config', @@ -1550,7 +1588,7 @@ class CommonConfigOpts(ConfigOpts): StrOpt('syslog-log-facility', default='LOG_USER', help='syslog facility to receive log lines') - ] + ] def __init__(self): super(CommonConfigOpts, self).__init__() diff --git a/nova/openstack/common/excutils.py b/nova/openstack/common/excutils.py index 3cb678e9d..67c9fa951 100644 --- a/nova/openstack/common/excutils.py +++ b/nova/openstack/common/excutils.py @@ -44,6 +44,6 @@ def save_and_reraise_exception(): yield except Exception: logging.error('Original exception being dropped: %s' % - (traceback.format_exception(type_, value, tb))) + (traceback.format_exception(type_, value, tb))) raise raise type_, value, tb diff --git a/nova/openstack/common/gettextutils.py b/nova/openstack/common/gettextutils.py new file mode 100644 index 000000000..235350cc4 --- /dev/null +++ b/nova/openstack/common/gettextutils.py @@ -0,0 +1,33 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Red Hat, Inc. +# 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. + +""" +gettext for openstack-common modules. + +Usual usage in an openstack.common module: + + from openstack.common.gettextutils import _ +""" + +import gettext + + +t = gettext.translation('openstack-common', 'locale', fallback=True) + + +def _(msg): + return t.ugettext(msg) diff --git a/nova/openstack/common/importutils.py b/nova/openstack/common/importutils.py index 7654af5b9..67d94ad5f 100644 --- a/nova/openstack/common/importutils.py +++ b/nova/openstack/common/importutils.py @@ -30,7 +30,7 @@ def import_class(import_str): return getattr(sys.modules[mod_str], class_str) except (ImportError, ValueError, AttributeError), exc: raise ImportError('Class %s cannot be found (%s)' % - (class_str, str(exc))) + (class_str, str(exc))) def import_object(import_str, *args, **kwargs): @@ -38,6 +38,19 @@ def import_object(import_str, *args, **kwargs): return import_class(import_str)(*args, **kwargs) +def import_object_ns(name_space, import_str, *args, **kwargs): + """ + Import a class and return an instance of it, first by trying + to find the class in a default namespace, then failing back to + a full path if not found in the default namespace. + """ + import_value = "%s.%s" % (name_space, import_str) + try: + return import_class(import_value)(*args, **kwargs) + except ImportError: + return import_class(import_str)(*args, **kwargs) + + def import_module(import_str): """Import a module.""" __import__(import_str) diff --git a/nova/openstack/common/iniparser.py b/nova/openstack/common/iniparser.py index 53ca02334..e91eea538 100644 --- a/nova/openstack/common/iniparser.py +++ b/nova/openstack/common/iniparser.py @@ -52,7 +52,10 @@ class BaseParser(object): else: key, value = line[:colon], line[colon + 1:] - return key.strip(), [value.strip()] + value = value.strip() + if value[0] == value[-1] and value[0] == "\"" or value[0] == "'": + value = value[1:-1] + return key.strip(), [value] def parse(self, lineiter): key = None diff --git a/nova/openstack/common/jsonutils.py b/nova/openstack/common/jsonutils.py index fa8b8f9d1..752266981 100644 --- a/nova/openstack/common/jsonutils.py +++ b/nova/openstack/common/jsonutils.py @@ -37,6 +37,7 @@ import datetime import inspect import itertools import json +import xmlrpclib def to_primitive(value, convert_instances=False, level=0): @@ -81,6 +82,12 @@ def to_primitive(value, convert_instances=False, level=0): # The try block may not be necessary after the class check above, # but just in case ... try: + # It's not clear why xmlrpclib created their own DateTime type, but + # for our purposes, make it a datetime type which is explicitly + # handled + if isinstance(value, xmlrpclib.DateTime): + value = datetime.datetime(*tuple(value.timetuple())[:6]) + if isinstance(value, (list, tuple)): o = [] for v in value: @@ -115,8 +122,8 @@ def to_primitive(value, convert_instances=False, level=0): return unicode(value) -def dumps(value): - return json.dumps(value, default=to_primitive) +def dumps(value, default=to_primitive, **kwargs): + return json.dumps(value, default=default, **kwargs) def loads(s): diff --git a/nova/log.py b/nova/openstack/common/log.py index 19ad95b70..42491b717 100644 --- a/nova/log.py +++ b/nova/openstack/common/log.py @@ -17,7 +17,7 @@ # License for the specific language governing permissions and limitations # under the License. -"""Nova logging handler. +"""Openstack 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 @@ -25,7 +25,7 @@ is not specified, default formatting is used. Additionally, an instance uuid may be passed as part of the log message, which is intended to make it easier for admins to find messages related to a specific instance. -It also allows setting of formatting information through flags. +It also allows setting of formatting information through conf. """ @@ -40,12 +40,10 @@ import stat import sys import traceback -import nova -from nova import flags +from nova import notifier from nova.openstack.common import cfg from nova.openstack.common import jsonutils from nova.openstack.common import local -from nova import version log_opts = [ @@ -92,8 +90,26 @@ log_opts = [ 'format it like this'), ] -FLAGS = flags.FLAGS -FLAGS.register_opts(log_opts) + +generic_log_opts = [ + cfg.StrOpt('logdir', + default=None, + help='Log output to a per-service log file in named directory'), + cfg.StrOpt('logfile', + default=None, + help='Log output to a named file'), + cfg.BoolOpt('use_stderr', + default=True, + help='Log output to standard error'), + cfg.StrOpt('logfile_mode', + default='0644', + help='Default file mode used when creating log files'), + ] + + +CONF = cfg.CONF +CONF.register_opts(generic_log_opts) +CONF.register_opts(log_opts) # our new audit level # NOTE(jkoelker) Since we synthesized an audit level, make the logging @@ -129,8 +145,8 @@ def _get_binary_name(): def _get_log_file_path(binary=None): - logfile = FLAGS.log_file or FLAGS.logfile - logdir = FLAGS.log_dir or FLAGS.logdir + logfile = CONF.log_file or CONF.logfile + logdir = CONF.log_dir or CONF.logdir if logfile and not logdir: return logfile @@ -143,11 +159,13 @@ def _get_log_file_path(binary=None): return '%s.log' % (os.path.join(logdir, binary),) -class NovaContextAdapter(logging.LoggerAdapter): +class ContextAdapter(logging.LoggerAdapter): warn = logging.LoggerAdapter.warning - def __init__(self, logger): + def __init__(self, logger, project_name, version_string): self.logger = logger + self.project = project_name + self.version = version_string def audit(self, msg, *args, **kwargs): self.log(logging.AUDIT, msg, *args, **kwargs) @@ -166,15 +184,16 @@ class NovaContextAdapter(logging.LoggerAdapter): instance = kwargs.pop('instance', None) instance_extra = '' if instance: - instance_extra = FLAGS.instance_format % instance + instance_extra = CONF.instance_format % instance else: instance_uuid = kwargs.pop('instance_uuid', None) if instance_uuid: - instance_extra = (FLAGS.instance_uuid_format + instance_extra = (CONF.instance_uuid_format % {'uuid': instance_uuid}) extra.update({'instance': instance_extra}) - extra.update({"nova_version": version.version_string_with_vcs()}) + extra.update({"project": self.project}) + extra.update({"version": self.version}) extra['extra'] = extra.copy() return msg, kwargs @@ -225,115 +244,47 @@ class JSONFormatter(logging.Formatter): return jsonutils.dumps(message) -class LegacyNovaFormatter(logging.Formatter): - """A nova.context.RequestContext aware formatter configured through flags. - - The flags used to set format strings are: logging_context_format_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 'instance' not in record.__dict__: - record.__dict__['instance'] = '' - - 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() - - if FLAGS.logging_exception_prefix.find('%(asctime)') != -1: - record.asctime = self.formatTime(record, self.datefmt) - - 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) - - -class NovaColorHandler(logging.StreamHandler): - LEVEL_COLORS = { - logging.DEBUG: '\033[00;32m', # GREEN - logging.INFO: '\033[00;36m', # CYAN - logging.AUDIT: '\033[01;36m', # BOLD CYAN - logging.WARN: '\033[01;33m', # BOLD YELLOW - logging.ERROR: '\033[01;31m', # BOLD RED - logging.CRITICAL: '\033[01;31m', # BOLD RED - } - - def format(self, record): - record.color = self.LEVEL_COLORS[record.levelno] - return logging.StreamHandler.format(self, record) - - class PublishErrorsHandler(logging.Handler): def emit(self, record): - if 'list_notifier_drivers' in FLAGS: - if 'nova.notifier.log_notifier' in FLAGS.list_notifier_drivers: + if 'list_notifier_drivers' in CONF: + if ('nova.openstack.common.notifier.log_notifier' in + CONF.list_notifier_drivers): return - nova.notifier.api.notify(None, 'nova.error.publisher', + notifier.api.notify(None, 'error.publisher', 'error_notification', - nova.notifier.api.ERROR, + notifier.api.ERROR, dict(error=record.msg)) def handle_exception(type, value, tb): extra = {} - if FLAGS.verbose: + if CONF.verbose: extra['exc_info'] = (type, value, tb) getLogger().critical(str(value), **extra) -def setup(): - """Setup nova logging.""" +def setup(product_name): + """Setup logging.""" sys.excepthook = handle_exception - if FLAGS.log_config: + if CONF.log_config: try: - logging.config.fileConfig(FLAGS.log_config) + logging.config.fileConfig(CONF.log_config) except Exception: traceback.print_exc() raise else: - _setup_logging_from_flags() + _setup_logging_from_conf(product_name) -def _find_facility_from_flags(): +def _find_facility_from_conf(): facility_names = logging.handlers.SysLogHandler.facility_names facility = getattr(logging.handlers.SysLogHandler, - FLAGS.syslog_log_facility, + CONF.syslog_log_facility, None) - if facility is None and FLAGS.syslog_log_facility in facility_names: - facility = facility_names.get(FLAGS.syslog_log_facility) + if facility is None and CONF.syslog_log_facility in facility_names: + facility = facility_names.get(CONF.syslog_log_facility) if facility is None: valid_facilities = facility_names.keys() @@ -350,59 +301,59 @@ def _find_facility_from_flags(): return facility -def _setup_logging_from_flags(): - nova_root = getLogger().logger - for handler in nova_root.handlers: - nova_root.removeHandler(handler) +def _setup_logging_from_conf(product_name): + log_root = getLogger(product_name).logger + for handler in log_root.handlers: + log_root.removeHandler(handler) - if FLAGS.use_syslog: - facility = _find_facility_from_flags() + if CONF.use_syslog: + facility = _find_facility_from_conf() syslog = logging.handlers.SysLogHandler(address='/dev/log', facility=facility) - nova_root.addHandler(syslog) + log_root.addHandler(syslog) logpath = _get_log_file_path() if logpath: filelog = logging.handlers.WatchedFileHandler(logpath) - nova_root.addHandler(filelog) + log_root.addHandler(filelog) - mode = int(FLAGS.logfile_mode, 8) + mode = int(CONF.logfile_mode, 8) st = os.stat(logpath) if st.st_mode != (stat.S_IFREG | mode): os.chmod(logpath, mode) - if FLAGS.use_stderr: - streamlog = NovaColorHandler() - nova_root.addHandler(streamlog) + if CONF.use_stderr: + streamlog = ColorHandler() + log_root.addHandler(streamlog) - elif not FLAGS.log_file: + elif not CONF.log_file: # pass sys.stdout as a positional argument # python2.6 calls the argument strm, in 2.7 it's stream streamlog = logging.StreamHandler(sys.stdout) - nova_root.addHandler(streamlog) + log_root.addHandler(streamlog) - if FLAGS.publish_errors: - nova_root.addHandler(PublishErrorsHandler(logging.ERROR)) + if CONF.publish_errors: + log_root.addHandler(PublishErrorsHandler(logging.ERROR)) - for handler in nova_root.handlers: - datefmt = FLAGS.log_date_format - if FLAGS.log_format: - handler.setFormatter(logging.Formatter(fmt=FLAGS.log_format, + for handler in log_root.handlers: + datefmt = CONF.log_date_format + if CONF.log_format: + handler.setFormatter(logging.Formatter(fmt=CONF.log_format, datefmt=datefmt)) - handler.setFormatter(LegacyNovaFormatter(datefmt=datefmt)) + handler.setFormatter(LegacyFormatter(datefmt=datefmt)) - if FLAGS.verbose or FLAGS.debug: - nova_root.setLevel(logging.DEBUG) + if CONF.verbose or CONF.debug: + log_root.setLevel(logging.DEBUG) else: - nova_root.setLevel(logging.INFO) + log_root.setLevel(logging.INFO) level = logging.NOTSET - for pair in FLAGS.default_log_levels: + for pair in CONF.default_log_levels: mod, _sep, level_name = pair.partition('=') level = logging.getLevelName(level_name) logger = logging.getLogger(mod) logger.setLevel(level) - for handler in nova_root.handlers: + for handler in log_root.handlers: logger.addHandler(handler) # NOTE(jkoelker) Clear the handlers for the root logger that was setup @@ -419,9 +370,11 @@ def _setup_logging_from_flags(): _loggers = {} -def getLogger(name='nova'): +def getLogger(name='unknown', version='unknown'): if name not in _loggers: - _loggers[name] = NovaContextAdapter(logging.getLogger(name)) + _loggers[name] = ContextAdapter(logging.getLogger(name), + name, + version) return _loggers[name] @@ -434,3 +387,72 @@ class WritableLogger(object): def write(self, msg): self.logger.log(self.level, msg) + + +class LegacyFormatter(logging.Formatter): + """A context.RequestContext aware formatter configured through flags. + + The flags used to set format strings are: logging_context_format_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 'instance' not in record.__dict__: + record.__dict__['instance'] = '' + + if record.__dict__.get('request_id', None): + self._fmt = CONF.logging_context_format_string + else: + self._fmt = CONF.logging_default_format_string + + if (record.levelno == logging.DEBUG and + CONF.logging_debug_format_suffix): + self._fmt += " " + CONF.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 CONF.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() + + if CONF.logging_exception_prefix.find('%(asctime)') != -1: + record.asctime = self.formatTime(record, self.datefmt) + + formatted_lines = [] + for line in lines: + pl = CONF.logging_exception_prefix % record.__dict__ + fl = '%s%s' % (pl, line) + formatted_lines.append(fl) + return '\n'.join(formatted_lines) + + +class ColorHandler(logging.StreamHandler): + LEVEL_COLORS = { + logging.DEBUG: '\033[00;32m', # GREEN + logging.INFO: '\033[00;36m', # CYAN + logging.AUDIT: '\033[01;36m', # BOLD CYAN + logging.WARN: '\033[01;33m', # BOLD YELLOW + logging.ERROR: '\033[01;31m', # BOLD RED + logging.CRITICAL: '\033[01;31m', # BOLD RED + } + + def format(self, record): + record.color = self.LEVEL_COLORS[record.levelno] + return logging.StreamHandler.format(self, record) diff --git a/nova/openstack/common/rpc/__init__.py b/nova/openstack/common/rpc/__init__.py index 6f022b3b2..1532c666e 100644 --- a/nova/openstack/common/rpc/__init__.py +++ b/nova/openstack/common/rpc/__init__.py @@ -56,7 +56,7 @@ rpc_opts = [ cfg.BoolOpt('fake_rabbit', default=False, help='If passed, use a fake RabbitMQ provider'), - ] +] cfg.CONF.register_opts(rpc_opts) diff --git a/nova/openstack/common/rpc/amqp.py b/nova/openstack/common/rpc/amqp.py index 5f62d35c5..510a03819 100644 --- a/nova/openstack/common/rpc/amqp.py +++ b/nova/openstack/common/rpc/amqp.py @@ -92,8 +92,9 @@ class ConnectionContext(rpc_common.Connection): if pooled: self.connection = connection_pool.get() else: - self.connection = connection_pool.connection_cls(conf, - server_params=server_params) + self.connection = connection_pool.connection_cls( + conf, + server_params=server_params) self.pooled = pooled def __enter__(self): @@ -161,8 +162,8 @@ def msg_reply(conf, msg_id, connection_pool, reply=None, failure=None, msg = {'result': reply, 'failure': failure} except TypeError: msg = {'result': dict((k, repr(v)) - for k, v in reply.__dict__.iteritems()), - 'failure': failure} + for k, v in reply.__dict__.iteritems()), + 'failure': failure} if ending: msg['ending'] = True conn.direct_send(msg_id, msg) @@ -288,8 +289,8 @@ class ProxyCallback(object): class MulticallWaiter(object): def __init__(self, conf, connection, timeout): self._connection = connection - self._iterator = connection.iterconsume( - timeout=timeout or conf.rpc_response_timeout) + self._iterator = connection.iterconsume(timeout=timeout or + conf.rpc_response_timeout) self._result = None self._done = False self._got_ending = False @@ -308,7 +309,7 @@ class MulticallWaiter(object): if data['failure']: failure = data['failure'] self._result = rpc_common.deserialize_remote_exception(self._conf, - failure) + failure) elif data.get('ending', False): self._got_ending = True @@ -389,16 +390,16 @@ def cast_to_server(conf, context, server_params, topic, msg, connection_pool): """Sends a message on a topic to a specific server.""" pack_context(msg, context) with ConnectionContext(conf, connection_pool, pooled=False, - server_params=server_params) as conn: + server_params=server_params) as conn: conn.topic_send(topic, msg) def fanout_cast_to_server(conf, context, server_params, topic, msg, - connection_pool): + connection_pool): """Sends a message on a fanout exchange to a specific server.""" pack_context(msg, context) with ConnectionContext(conf, connection_pool, pooled=False, - server_params=server_params) as conn: + server_params=server_params) as conn: conn.fanout_send(topic, msg) diff --git a/nova/openstack/common/rpc/common.py b/nova/openstack/common/rpc/common.py index 0b927a0ee..7bc65a6a6 100644 --- a/nova/openstack/common/rpc/common.py +++ b/nova/openstack/common/rpc/common.py @@ -23,6 +23,7 @@ import sys import traceback from nova.openstack.common import cfg +from nova.openstack.common.gettextutils import _ from nova.openstack.common import importutils from nova.openstack.common import jsonutils from nova.openstack.common import local @@ -167,10 +168,8 @@ class Connection(object): def _safe_log(log_func, msg, msg_data): """Sanitizes the msg_data field before logging.""" - SANITIZE = { - 'set_admin_password': ('new_pass',), - 'run_instance': ('admin_password',), - } + SANITIZE = {'set_admin_password': ('new_pass',), + 'run_instance': ('admin_password',), } has_method = 'method' in msg_data and msg_data['method'] in SANITIZE has_context_token = '_context_auth_token' in msg_data diff --git a/nova/openstack/common/rpc/dispatcher.py b/nova/openstack/common/rpc/dispatcher.py index 1d36f32ea..1fdb16c0b 100644 --- a/nova/openstack/common/rpc/dispatcher.py +++ b/nova/openstack/common/rpc/dispatcher.py @@ -92,14 +92,20 @@ class RpcDispatcher(object): if not version: version = '1.0' + had_compatible = False for proxyobj in self.callbacks: if hasattr(proxyobj, 'RPC_API_VERSION'): rpc_api_version = proxyobj.RPC_API_VERSION else: rpc_api_version = '1.0' + is_compatible = self._is_compatible(rpc_api_version, version) + had_compatible = had_compatible or is_compatible if not hasattr(proxyobj, method): continue - if self._is_compatible(rpc_api_version, version): + if is_compatible: return getattr(proxyobj, method)(ctxt, **kwargs) - raise rpc_common.UnsupportedRpcVersion(version=version) + if had_compatible: + raise AttributeError("No such RPC function '%s'" % method) + else: + raise rpc_common.UnsupportedRpcVersion(version=version) diff --git a/nova/openstack/common/rpc/impl_kombu.py b/nova/openstack/common/rpc/impl_kombu.py index 66152a9d5..fff1ed9ce 100644 --- a/nova/openstack/common/rpc/impl_kombu.py +++ b/nova/openstack/common/rpc/impl_kombu.py @@ -30,6 +30,7 @@ import kombu.entity import kombu.messaging from nova.openstack.common import cfg +from nova.openstack.common.gettextutils import _ from nova.openstack.common.rpc import amqp as rpc_amqp from nova.openstack.common.rpc import common as rpc_common @@ -46,7 +47,7 @@ kombu_opts = [ cfg.StrOpt('kombu_ssl_ca_certs', default='', help=('SSL certification authority file ' - '(valid only if SSL enabled)')), + '(valid only if SSL enabled)')), cfg.StrOpt('rabbit_host', default='localhost', help='the RabbitMQ host'), @@ -80,7 +81,7 @@ kombu_opts = [ default=False, help='use durable queues in RabbitMQ'), - ] +] cfg.CONF.register_opts(kombu_opts) @@ -171,22 +172,20 @@ class DirectConsumer(ConsumerBase): """ # Default options options = {'durable': False, - 'auto_delete': True, - 'exclusive': True} + 'auto_delete': True, + 'exclusive': True} options.update(kwargs) - exchange = kombu.entity.Exchange( - name=msg_id, - type='direct', - durable=options['durable'], - auto_delete=options['auto_delete']) - super(DirectConsumer, self).__init__( - channel, - callback, - tag, - name=msg_id, - exchange=exchange, - routing_key=msg_id, - **options) + exchange = kombu.entity.Exchange(name=msg_id, + type='direct', + durable=options['durable'], + auto_delete=options['auto_delete']) + super(DirectConsumer, self).__init__(channel, + callback, + tag, + name=msg_id, + exchange=exchange, + routing_key=msg_id, + **options) class TopicConsumer(ConsumerBase): @@ -208,22 +207,20 @@ class TopicConsumer(ConsumerBase): """ # Default options options = {'durable': conf.rabbit_durable_queues, - 'auto_delete': False, - 'exclusive': False} + 'auto_delete': False, + 'exclusive': False} options.update(kwargs) - exchange = kombu.entity.Exchange( - name=conf.control_exchange, - type='topic', - durable=options['durable'], - auto_delete=options['auto_delete']) - super(TopicConsumer, self).__init__( - channel, - callback, - tag, - name=name or topic, - exchange=exchange, - routing_key=topic, - **options) + exchange = kombu.entity.Exchange(name=conf.control_exchange, + type='topic', + durable=options['durable'], + auto_delete=options['auto_delete']) + super(TopicConsumer, self).__init__(channel, + callback, + tag, + name=name or topic, + exchange=exchange, + routing_key=topic, + **options) class FanoutConsumer(ConsumerBase): @@ -245,22 +242,17 @@ class FanoutConsumer(ConsumerBase): # Default options options = {'durable': False, - 'auto_delete': True, - 'exclusive': True} + 'auto_delete': True, + 'exclusive': True} options.update(kwargs) - exchange = kombu.entity.Exchange( - name=exchange_name, - type='fanout', - durable=options['durable'], - auto_delete=options['auto_delete']) - super(FanoutConsumer, self).__init__( - channel, - callback, - tag, - name=queue_name, - exchange=exchange, - routing_key=topic, - **options) + exchange = kombu.entity.Exchange(name=exchange_name, type='fanout', + durable=options['durable'], + auto_delete=options['auto_delete']) + super(FanoutConsumer, self).__init__(channel, callback, tag, + name=queue_name, + exchange=exchange, + routing_key=topic, + **options) class Publisher(object): @@ -278,9 +270,10 @@ class Publisher(object): def reconnect(self, channel): """Re-establish the Producer after a rabbit reconnection""" self.exchange = kombu.entity.Exchange(name=self.exchange_name, - **self.kwargs) + **self.kwargs) self.producer = kombu.messaging.Producer(exchange=self.exchange, - channel=channel, routing_key=self.routing_key) + channel=channel, + routing_key=self.routing_key) def send(self, msg): """Send a message""" @@ -296,14 +289,11 @@ class DirectPublisher(Publisher): """ options = {'durable': False, - 'auto_delete': True, - 'exclusive': True} + 'auto_delete': True, + 'exclusive': True} options.update(kwargs) - super(DirectPublisher, self).__init__(channel, - msg_id, - msg_id, - type='direct', - **options) + super(DirectPublisher, self).__init__(channel, msg_id, msg_id, + type='direct', **options) class TopicPublisher(Publisher): @@ -314,14 +304,11 @@ class TopicPublisher(Publisher): Kombu options may be passed as keyword args to override defaults """ options = {'durable': conf.rabbit_durable_queues, - 'auto_delete': False, - 'exclusive': False} + 'auto_delete': False, + 'exclusive': False} options.update(kwargs) - super(TopicPublisher, self).__init__(channel, - conf.control_exchange, - topic, - type='topic', - **options) + super(TopicPublisher, self).__init__(channel, conf.control_exchange, + topic, type='topic', **options) class FanoutPublisher(Publisher): @@ -332,14 +319,11 @@ class FanoutPublisher(Publisher): Kombu options may be passed as keyword args to override defaults """ options = {'durable': False, - 'auto_delete': True, - 'exclusive': True} + 'auto_delete': True, + 'exclusive': True} options.update(kwargs) - super(FanoutPublisher, self).__init__(channel, - '%s_fanout' % topic, - None, - type='fanout', - **options) + super(FanoutPublisher, self).__init__(channel, '%s_fanout' % topic, + None, type='fanout', **options) class NotifyPublisher(TopicPublisher): @@ -356,10 +340,10 @@ class NotifyPublisher(TopicPublisher): # we do this to ensure that messages don't get dropped if the # consumer is started after we do queue = kombu.entity.Queue(channel=channel, - exchange=self.exchange, - durable=self.durable, - name=self.routing_key, - routing_key=self.routing_key) + exchange=self.exchange, + durable=self.durable, + name=self.routing_key, + routing_key=self.routing_key) queue.declare() @@ -445,7 +429,7 @@ class Connection(object): """ if self.connection: LOG.info(_("Reconnecting to AMQP server on " - "%(hostname)s:%(port)d") % self.params) + "%(hostname)s:%(port)d") % self.params) try: self.connection.close() except self.connection_errors: @@ -453,8 +437,7 @@ class Connection(object): # Setting this in case the next statement fails, though # it shouldn't be doing any network operations, yet. self.connection = None - self.connection = kombu.connection.BrokerConnection( - **self.params) + self.connection = kombu.connection.BrokerConnection(**self.params) self.connection_errors = self.connection.connection_errors if self.memory_transport: # Kludge to speed up tests. @@ -504,8 +487,8 @@ class Connection(object): if self.max_retries and attempt == self.max_retries: LOG.exception(_('Unable to connect to AMQP server on ' - '%(hostname)s:%(port)d after %(max_retries)d ' - 'tries: %(err_str)s') % log_info) + '%(hostname)s:%(port)d after %(max_retries)d ' + 'tries: %(err_str)s') % log_info) # NOTE(comstud): Copied from original code. There's # really no better recourse because if this was a queue we # need to consume on, we have no way to consume anymore. @@ -519,9 +502,9 @@ class Connection(object): sleep_time = min(sleep_time, self.interval_max) log_info['sleep_time'] = sleep_time - LOG.warn(_('AMQP server on %(hostname)s:%(port)d is' - ' unreachable: %(err_str)s. Trying again in ' - '%(sleep_time)d seconds.') % log_info) + LOG.exception(_('AMQP server on %(hostname)s:%(port)d is' + ' unreachable: %(err_str)s. Trying again in ' + '%(sleep_time)d seconds.') % log_info) time.sleep(sleep_time) def ensure(self, error_callback, method, *args, **kwargs): @@ -571,11 +554,11 @@ class Connection(object): def _connect_error(exc): log_info = {'topic': topic, 'err_str': str(exc)} LOG.error(_("Failed to declare consumer for topic '%(topic)s': " - "%(err_str)s") % log_info) + "%(err_str)s") % log_info) def _declare_consumer(): consumer = consumer_cls(self.conf, self.channel, topic, callback, - self.consumer_num.next()) + self.consumer_num.next()) self.consumers.append(consumer) return consumer @@ -589,11 +572,11 @@ class Connection(object): def _error_callback(exc): if isinstance(exc, socket.timeout): LOG.exception(_('Timed out waiting for RPC response: %s') % - str(exc)) + str(exc)) raise rpc_common.Timeout() else: LOG.exception(_('Failed to consume message from queue: %s') % - str(exc)) + str(exc)) info['do_consume'] = True def _consume(): @@ -627,7 +610,7 @@ class Connection(object): def _error_callback(exc): log_info = {'topic': topic, 'err_str': str(exc)} LOG.exception(_("Failed to publish message to topic " - "'%(topic)s': %(err_str)s") % log_info) + "'%(topic)s': %(err_str)s") % log_info) def _publish(): publisher = cls(self.conf, self.channel, topic, **kwargs) @@ -691,8 +674,9 @@ class Connection(object): def create_consumer(self, topic, proxy, fanout=False): """Create a consumer that calls a method in a proxy object""" - proxy_cb = rpc_amqp.ProxyCallback(self.conf, proxy, - rpc_amqp.get_connection_pool(self.conf, Connection)) + proxy_cb = rpc_amqp.ProxyCallback( + self.conf, proxy, + rpc_amqp.get_connection_pool(self.conf, Connection)) if fanout: self.declare_fanout_consumer(topic, proxy_cb) @@ -701,57 +685,66 @@ class Connection(object): def create_worker(self, topic, proxy, pool_name): """Create a worker that calls a method in a proxy object""" - proxy_cb = rpc_amqp.ProxyCallback(self.conf, proxy, - rpc_amqp.get_connection_pool(self.conf, Connection)) + proxy_cb = rpc_amqp.ProxyCallback( + self.conf, proxy, + rpc_amqp.get_connection_pool(self.conf, Connection)) self.declare_topic_consumer(topic, proxy_cb, pool_name) def create_connection(conf, new=True): """Create a connection""" - return rpc_amqp.create_connection(conf, new, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.create_connection( + conf, new, + rpc_amqp.get_connection_pool(conf, Connection)) def multicall(conf, context, topic, msg, timeout=None): """Make a call that returns multiple times.""" - return rpc_amqp.multicall(conf, context, topic, msg, timeout, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.multicall( + conf, context, topic, msg, timeout, + rpc_amqp.get_connection_pool(conf, Connection)) def call(conf, context, topic, msg, timeout=None): """Sends a message on a topic and wait for a response.""" - return rpc_amqp.call(conf, context, topic, msg, timeout, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.call( + conf, context, topic, msg, timeout, + rpc_amqp.get_connection_pool(conf, Connection)) def cast(conf, context, topic, msg): """Sends a message on a topic without waiting for a response.""" - return rpc_amqp.cast(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.cast( + conf, context, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def fanout_cast(conf, context, topic, msg): """Sends a message on a fanout exchange without waiting for a response.""" - return rpc_amqp.fanout_cast(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.fanout_cast( + conf, context, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def cast_to_server(conf, context, server_params, topic, msg): """Sends a message on a topic to a specific server.""" - return rpc_amqp.cast_to_server(conf, context, server_params, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.cast_to_server( + conf, context, server_params, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def fanout_cast_to_server(conf, context, server_params, topic, msg): """Sends a message on a fanout exchange to a specific server.""" - return rpc_amqp.cast_to_server(conf, context, server_params, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.cast_to_server( + conf, context, server_params, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def notify(conf, context, topic, msg): """Sends a notification event on a topic.""" - return rpc_amqp.notify(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.notify( + conf, context, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def cleanup(): diff --git a/nova/openstack/common/rpc/impl_qpid.py b/nova/openstack/common/rpc/impl_qpid.py index fce8cd07b..2eb1dfa7e 100644 --- a/nova/openstack/common/rpc/impl_qpid.py +++ b/nova/openstack/common/rpc/impl_qpid.py @@ -77,7 +77,7 @@ qpid_opts = [ cfg.BoolOpt('qpid_tcp_nodelay', default=True, help='Disable Nagle algorithm'), - ] +] cfg.CONF.register_opts(qpid_opts) @@ -161,10 +161,10 @@ class DirectConsumer(ConsumerBase): """ super(DirectConsumer, self).__init__(session, callback, - "%s/%s" % (msg_id, msg_id), - {"type": "direct"}, - msg_id, - {"exclusive": True}) + "%s/%s" % (msg_id, msg_id), + {"type": "direct"}, + msg_id, + {"exclusive": True}) class TopicConsumer(ConsumerBase): @@ -181,8 +181,9 @@ class TopicConsumer(ConsumerBase): """ super(TopicConsumer, self).__init__(session, callback, - "%s/%s" % (conf.control_exchange, topic), {}, - name or topic, {}) + "%s/%s" % (conf.control_exchange, + topic), + {}, name or topic, {}) class FanoutConsumer(ConsumerBase): @@ -196,11 +197,12 @@ class FanoutConsumer(ConsumerBase): 'callback' is the callback to call when messages are received """ - super(FanoutConsumer, self).__init__(session, callback, - "%s_fanout" % topic, - {"durable": False, "type": "fanout"}, - "%s_fanout_%s" % (topic, uuid.uuid4().hex), - {"exclusive": True}) + super(FanoutConsumer, self).__init__( + session, callback, + "%s_fanout" % topic, + {"durable": False, "type": "fanout"}, + "%s_fanout_%s" % (topic, uuid.uuid4().hex), + {"exclusive": True}) class Publisher(object): @@ -254,8 +256,9 @@ class TopicPublisher(Publisher): def __init__(self, conf, session, topic): """init a 'topic' publisher. """ - super(TopicPublisher, self).__init__(session, - "%s/%s" % (conf.control_exchange, topic)) + super(TopicPublisher, self).__init__( + session, + "%s/%s" % (conf.control_exchange, topic)) class FanoutPublisher(Publisher): @@ -263,8 +266,9 @@ class FanoutPublisher(Publisher): def __init__(self, conf, session, topic): """init a 'fanout' publisher. """ - super(FanoutPublisher, self).__init__(session, - "%s_fanout" % topic, {"type": "fanout"}) + super(FanoutPublisher, self).__init__( + session, + "%s_fanout" % topic, {"type": "fanout"}) class NotifyPublisher(Publisher): @@ -272,9 +276,10 @@ class NotifyPublisher(Publisher): def __init__(self, conf, session, topic): """init a 'topic' publisher. """ - super(NotifyPublisher, self).__init__(session, - "%s/%s" % (conf.control_exchange, topic), - {"durable": True}) + super(NotifyPublisher, self).__init__( + session, + "%s/%s" % (conf.control_exchange, topic), + {"durable": True}) class Connection(object): @@ -292,9 +297,9 @@ class Connection(object): server_params = {} default_params = dict(hostname=self.conf.qpid_hostname, - port=self.conf.qpid_port, - username=self.conf.qpid_username, - password=self.conf.qpid_password) + port=self.conf.qpid_port, + username=self.conf.qpid_username, + password=self.conf.qpid_password) params = server_params for key in default_params.keys(): @@ -312,18 +317,18 @@ class Connection(object): self.connection.reconnect = self.conf.qpid_reconnect if self.conf.qpid_reconnect_timeout: self.connection.reconnect_timeout = ( - self.conf.qpid_reconnect_timeout) + self.conf.qpid_reconnect_timeout) if self.conf.qpid_reconnect_limit: self.connection.reconnect_limit = self.conf.qpid_reconnect_limit if self.conf.qpid_reconnect_interval_max: self.connection.reconnect_interval_max = ( - self.conf.qpid_reconnect_interval_max) + self.conf.qpid_reconnect_interval_max) if self.conf.qpid_reconnect_interval_min: self.connection.reconnect_interval_min = ( - self.conf.qpid_reconnect_interval_min) + self.conf.qpid_reconnect_interval_min) if self.conf.qpid_reconnect_interval: self.connection.reconnect_interval = ( - self.conf.qpid_reconnect_interval) + self.conf.qpid_reconnect_interval) self.connection.hearbeat = self.conf.qpid_heartbeat self.connection.protocol = self.conf.qpid_protocol self.connection.tcp_nodelay = self.conf.qpid_tcp_nodelay @@ -395,7 +400,7 @@ class Connection(object): def _connect_error(exc): log_info = {'topic': topic, 'err_str': str(exc)} LOG.error(_("Failed to declare consumer for topic '%(topic)s': " - "%(err_str)s") % log_info) + "%(err_str)s") % log_info) def _declare_consumer(): consumer = consumer_cls(self.conf, self.session, topic, callback) @@ -410,11 +415,11 @@ class Connection(object): def _error_callback(exc): if isinstance(exc, qpid.messaging.exceptions.Empty): LOG.exception(_('Timed out waiting for RPC response: %s') % - str(exc)) + str(exc)) raise rpc_common.Timeout() else: LOG.exception(_('Failed to consume message from queue: %s') % - str(exc)) + str(exc)) def _consume(): nxt_receiver = self.session.next_receiver(timeout=timeout) @@ -444,7 +449,7 @@ class Connection(object): def _connect_error(exc): log_info = {'topic': topic, 'err_str': str(exc)} LOG.exception(_("Failed to publish message to topic " - "'%(topic)s': %(err_str)s") % log_info) + "'%(topic)s': %(err_str)s") % log_info) def _publisher_send(): publisher = cls(self.conf, self.session, topic) @@ -508,8 +513,9 @@ class Connection(object): def create_consumer(self, topic, proxy, fanout=False): """Create a consumer that calls a method in a proxy object""" - proxy_cb = rpc_amqp.ProxyCallback(self.conf, proxy, - rpc_amqp.get_connection_pool(self.conf, Connection)) + proxy_cb = rpc_amqp.ProxyCallback( + self.conf, proxy, + rpc_amqp.get_connection_pool(self.conf, Connection)) if fanout: consumer = FanoutConsumer(self.conf, self.session, topic, proxy_cb) @@ -522,8 +528,9 @@ class Connection(object): def create_worker(self, topic, proxy, pool_name): """Create a worker that calls a method in a proxy object""" - proxy_cb = rpc_amqp.ProxyCallback(self.conf, proxy, - rpc_amqp.get_connection_pool(self.conf, Connection)) + proxy_cb = rpc_amqp.ProxyCallback( + self.conf, proxy, + rpc_amqp.get_connection_pool(self.conf, Connection)) consumer = TopicConsumer(self.conf, self.session, topic, proxy_cb, name=pool_name) @@ -535,50 +542,57 @@ class Connection(object): def create_connection(conf, new=True): """Create a connection""" - return rpc_amqp.create_connection(conf, new, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.create_connection( + conf, new, + rpc_amqp.get_connection_pool(conf, Connection)) def multicall(conf, context, topic, msg, timeout=None): """Make a call that returns multiple times.""" - return rpc_amqp.multicall(conf, context, topic, msg, timeout, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.multicall( + conf, context, topic, msg, timeout, + rpc_amqp.get_connection_pool(conf, Connection)) def call(conf, context, topic, msg, timeout=None): """Sends a message on a topic and wait for a response.""" - return rpc_amqp.call(conf, context, topic, msg, timeout, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.call( + conf, context, topic, msg, timeout, + rpc_amqp.get_connection_pool(conf, Connection)) def cast(conf, context, topic, msg): """Sends a message on a topic without waiting for a response.""" - return rpc_amqp.cast(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.cast( + conf, context, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def fanout_cast(conf, context, topic, msg): """Sends a message on a fanout exchange without waiting for a response.""" - return rpc_amqp.fanout_cast(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.fanout_cast( + conf, context, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def cast_to_server(conf, context, server_params, topic, msg): """Sends a message on a topic to a specific server.""" - return rpc_amqp.cast_to_server(conf, context, server_params, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.cast_to_server( + conf, context, server_params, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def fanout_cast_to_server(conf, context, server_params, topic, msg): """Sends a message on a fanout exchange to a specific server.""" - return rpc_amqp.fanout_cast_to_server(conf, context, server_params, topic, - msg, rpc_amqp.get_connection_pool(conf, Connection)) + return rpc_amqp.fanout_cast_to_server( + conf, context, server_params, topic, msg, + rpc_amqp.get_connection_pool(conf, Connection)) def notify(conf, context, topic, msg): """Sends a notification event on a topic.""" return rpc_amqp.notify(conf, context, topic, msg, - rpc_amqp.get_connection_pool(conf, Connection)) + rpc_amqp.get_connection_pool(conf, Connection)) def cleanup(): diff --git a/nova/openstack/common/rpc/impl_zmq.py b/nova/openstack/common/rpc/impl_zmq.py index 77768dbec..dc5491381 100644 --- a/nova/openstack/common/rpc/impl_zmq.py +++ b/nova/openstack/common/rpc/impl_zmq.py @@ -40,25 +40,26 @@ RPCException = rpc_common.RPCException zmq_opts = [ cfg.StrOpt('rpc_zmq_bind_address', default='*', - help='ZeroMQ bind address. Should be a wildcard (*), ' - 'an ethernet interface, or IP. ' - 'The "host" option should point or resolve to this address.'), + help='ZeroMQ bind address. Should be a wildcard (*), ' + 'an ethernet interface, or IP. ' + 'The "host" option should point or resolve to this ' + 'address.'), # The module.Class to use for matchmaking. cfg.StrOpt('rpc_zmq_matchmaker', - default='openstack.common.rpc.matchmaker.MatchMakerLocalhost', + default='nova.openstack.common.rpc.matchmaker.MatchMakerLocalhost', help='MatchMaker driver'), # The following port is unassigned by IANA as of 2012-05-21 cfg.IntOpt('rpc_zmq_port', default=9501, - help='ZeroMQ receiver listening port'), + help='ZeroMQ receiver listening port'), cfg.IntOpt('rpc_zmq_contexts', default=1, - help='Number of ZeroMQ contexts, defaults to 1'), + help='Number of ZeroMQ contexts, defaults to 1'), cfg.StrOpt('rpc_zmq_ipc_dir', default='/var/run/openstack', - help='Directory for holding IPC sockets'), - ] + help='Directory for holding IPC sockets'), +] # These globals are defined in register_opts(conf), @@ -119,10 +120,10 @@ class ZmqSocket(object): self.subscribe(f) LOG.debug(_("Connecting to %{addr}s with %{type}s" - "\n-> Subscribed to %{subscribe}s" - "\n-> bind: %{bind}s"), - {'addr': addr, 'type': self.socket_s(), - 'subscribe': subscribe, 'bind': bind}) + "\n-> Subscribed to %{subscribe}s" + "\n-> bind: %{bind}s"), + {'addr': addr, 'type': self.socket_s(), + 'subscribe': subscribe, 'bind': bind}) try: if bind: @@ -197,7 +198,7 @@ class ZmqClient(object): def cast(self, msg_id, topic, data): self.outq.send([str(msg_id), str(topic), str('cast'), - _serialize(data)]) + _serialize(data)]) def close(self): self.outq.close() @@ -306,7 +307,7 @@ class ConsumerBase(object): data.setdefault('version', None) data.setdefault('args', []) proxy.dispatch(ctx, data['version'], - data['method'], **data['args']) + data['method'], **data['args']) class ZmqBaseReactor(ConsumerBase): @@ -339,7 +340,7 @@ class ZmqBaseReactor(ConsumerBase): # Items push in. inq = ZmqSocket(in_addr, zmq_type_in, bind=in_bind, - subscribe=subscribe) + subscribe=subscribe) self.proxies[inq] = proxy self.sockets.append(inq) @@ -353,8 +354,7 @@ class ZmqBaseReactor(ConsumerBase): raise RPCException("Bad output socktype") # Items push out. - outq = ZmqSocket(out_addr, zmq_type_out, - bind=out_bind) + outq = ZmqSocket(out_addr, zmq_type_out, bind=out_bind) self.mapping[inq] = outq self.mapping[outq] = inq @@ -428,7 +428,7 @@ class ZmqProxy(ZmqBaseReactor): if not topic in self.topic_proxy: outq = ZmqSocket("ipc://%s/zmq_topic_%s" % (ipc_dir, topic), - sock_type, bind=True) + sock_type, bind=True) self.topic_proxy[topic] = outq self.sockets.append(outq) LOG.info(_("Created topic proxy: %s"), topic) @@ -486,7 +486,7 @@ class Connection(rpc_common.Connection): topic = topic.split('.', 1)[0] LOG.info(_("Create Consumer for topic (%(topic)s)") % - {'topic': topic}) + {'topic': topic}) # Subscription scenarios if fanout: @@ -502,7 +502,7 @@ class Connection(rpc_common.Connection): (self.conf.rpc_zmq_ipc_dir, topic) LOG.debug(_("Consumer is a zmq.%s"), - ['PULL', 'SUB'][sock_type == zmq.SUB]) + ['PULL', 'SUB'][sock_type == zmq.SUB]) self.reactor.register(proxy, inaddr, sock_type, subscribe=subscribe, in_bind=False) diff --git a/nova/openstack/common/rpc/matchmaker.py b/nova/openstack/common/rpc/matchmaker.py index f59e2555d..fe9896a04 100644 --- a/nova/openstack/common/rpc/matchmaker.py +++ b/nova/openstack/common/rpc/matchmaker.py @@ -29,8 +29,8 @@ from nova.openstack.common import cfg matchmaker_opts = [ # Matchmaker ring file cfg.StrOpt('matchmaker_ringfile', - default='/etc/nova/matchmaker_ring.json', - help='Matchmaker ring file (JSON)'), + default='/etc/nova/matchmaker_ring.json', + help='Matchmaker ring file (JSON)'), ] CONF = cfg.CONF diff --git a/nova/openstack/common/rpc/proxy.py b/nova/openstack/common/rpc/proxy.py index cfa2b8cd0..fe28cdde5 100644 --- a/nova/openstack/common/rpc/proxy.py +++ b/nova/openstack/common/rpc/proxy.py @@ -127,7 +127,7 @@ class RpcProxy(object): rpc.fanout_cast(context, self.topic, msg) def cast_to_server(self, context, server_params, msg, topic=None, - version=None): + version=None): """rpc.cast_to_server() a remote method. :param context: The request context diff --git a/nova/quota.py b/nova/quota.py index 68321cf74..d3ba0aa02 100644 --- a/nova/quota.py +++ b/nova/quota.py @@ -23,9 +23,9 @@ import datetime from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py index 64f068ff2..be39d5a0a 100644 --- a/nova/scheduler/driver.py +++ b/nova/scheduler/driver.py @@ -28,11 +28,11 @@ from nova.compute import task_states from nova import db from nova import exception from nova import flags -from nova import log as logging from nova import notifications from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common import timeutils from nova import utils diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py index c577e3569..c186ceeb4 100644 --- a/nova/scheduler/filter_scheduler.py +++ b/nova/scheduler/filter_scheduler.py @@ -23,9 +23,9 @@ import operator from nova import exception from nova import flags -from nova import log as logging from nova.notifier import api as notifier from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.scheduler import driver from nova.scheduler import least_cost from nova.scheduler import scheduler_options diff --git a/nova/scheduler/filters/arch_filter.py b/nova/scheduler/filters/arch_filter.py index 1f11d07b6..625ce2909 100644 --- a/nova/scheduler/filters/arch_filter.py +++ b/nova/scheduler/filters/arch_filter.py @@ -15,7 +15,7 @@ # under the License. -from nova import log as logging +from nova.openstack.common import log as logging from nova.scheduler import filters from nova import utils diff --git a/nova/scheduler/filters/compute_filter.py b/nova/scheduler/filters/compute_filter.py index 5409d3db0..5ba078394 100644 --- a/nova/scheduler/filters/compute_filter.py +++ b/nova/scheduler/filters/compute_filter.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from nova import log as logging +from nova.openstack.common import log as logging from nova.scheduler import filters from nova import utils diff --git a/nova/scheduler/filters/core_filter.py b/nova/scheduler/filters/core_filter.py index 5abcfeec7..5af68bc9f 100644 --- a/nova/scheduler/filters/core_filter.py +++ b/nova/scheduler/filters/core_filter.py @@ -16,8 +16,8 @@ # under the License. from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.scheduler import filters diff --git a/nova/scheduler/filters/ram_filter.py b/nova/scheduler/filters/ram_filter.py index 4bc84c243..7f79c1923 100644 --- a/nova/scheduler/filters/ram_filter.py +++ b/nova/scheduler/filters/ram_filter.py @@ -15,8 +15,8 @@ # under the License. from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.scheduler import filters LOG = logging.getLogger(__name__) diff --git a/nova/scheduler/filters/trusted_filter.py b/nova/scheduler/filters/trusted_filter.py index 41dc18dc3..a7c711b4e 100644 --- a/nova/scheduler/filters/trusted_filter.py +++ b/nova/scheduler/filters/trusted_filter.py @@ -49,9 +49,9 @@ import socket import ssl from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.scheduler import filters diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index 6de5ebc8f..19f0a0bb7 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -23,8 +23,8 @@ import UserDict from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova.scheduler import filters diff --git a/nova/scheduler/least_cost.py b/nova/scheduler/least_cost.py index 45b76bb47..71db730cb 100644 --- a/nova/scheduler/least_cost.py +++ b/nova/scheduler/least_cost.py @@ -23,8 +23,8 @@ is then selected for provisioning. """ from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index e24705a02..436df0946 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -27,13 +27,13 @@ from nova.compute import vm_states 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 notifications from nova.notifier import api as notifier from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import quota diff --git a/nova/scheduler/scheduler_options.py b/nova/scheduler/scheduler_options.py index 94572fc69..7acf2f750 100644 --- a/nova/scheduler/scheduler_options.py +++ b/nova/scheduler/scheduler_options.py @@ -27,8 +27,8 @@ import json import os from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import timeutils diff --git a/nova/service.py b/nova/service.py index d3fb76933..bb2964b95 100644 --- a/nova/service.py +++ b/nova/service.py @@ -35,9 +35,9 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import utils from nova import version diff --git a/nova/test.py b/nova/test.py index da115300e..a8a2464ce 100644 --- a/nova/test.py +++ b/nova/test.py @@ -32,8 +32,8 @@ import nose.plugins.skip import stubout from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import service from nova import tests diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index 9ff38e3a9..a8d0ef28c 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -39,7 +39,7 @@ import shutil from nova.db.sqlalchemy.session import get_engine from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging import eventlet @@ -49,7 +49,7 @@ eventlet.monkey_patch(os=False) FLAGS = flags.FLAGS FLAGS.use_stderr = False -logging.setup() +logging.setup('nova') _DB = None diff --git a/nova/tests/api/ec2/test_cinder_cloud.py b/nova/tests/api/ec2/test_cinder_cloud.py new file mode 100644 index 000000000..dd99a45dd --- /dev/null +++ b/nova/tests/api/ec2/test_cinder_cloud.py @@ -0,0 +1,939 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. +# 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 copy + +from nova.api.ec2 import cloud +from nova.api.ec2 import ec2utils +from nova.compute import api as compute_api +from nova.compute import utils as compute_utils +from nova import context +from nova import db +from nova import exception +from nova import flags +from nova.openstack.common import log as logging +from nova.openstack.common import rpc +from nova import test +from nova.tests.image import fake +from nova import volume + + +LOG = logging.getLogger(__name__) +FLAGS = flags.FLAGS + + +def get_fake_cache(): + def _ip(ip, fixed=True, floats=None): + ip_dict = {'address': ip, 'type': 'fixed'} + if not fixed: + ip_dict['type'] = 'floating' + if fixed and floats: + ip_dict['floating_ips'] = [_ip(f, fixed=False) for f in floats] + return ip_dict + + info = [{'address': 'aa:bb:cc:dd:ee:ff', + 'id': 1, + 'network': {'bridge': 'br0', + 'id': 1, + 'label': 'private', + 'subnets': [{'cidr': '192.168.0.0/24', + 'ips': [_ip('192.168.0.3', + floats=['1.2.3.4', + '5.6.7.8']), + _ip('192.168.0.4')]}]}}] + if FLAGS.use_ipv6: + ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff' + info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64', + 'ips': [_ip(ipv6_addr)]}) + return info + + +def get_instances_with_cached_ips(orig_func, *args, **kwargs): + """Kludge the cache into instance(s) without having to create DB + entries + """ + instances = orig_func(*args, **kwargs) + if isinstance(instances, list): + for instance in instances: + instance['info_cache'] = {'network_info': get_fake_cache()} + else: + instances['info_cache'] = {'network_info': get_fake_cache()} + return instances + + +class CinderCloudTestCase(test.TestCase): + def setUp(self): + super(CinderCloudTestCase, self).setUp() + self.flags(compute_driver='nova.virt.fake.FakeDriver', + volume_api_class='nova.tests.fake_volume.API', + stub_network=True) + + def fake_show(meh, context, id): + return {'id': id, + 'container_format': 'ami', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'ramdisk_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine', + 'image_state': 'available'}} + + def fake_detail(_self, context, **kwargs): + image = fake_show(None, context, None) + image['name'] = kwargs.get('filters', {}).get('name') + return [image] + + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'detail', fake_detail) + fake.stub_out_image_service(self.stubs) + + def dumb(*args, **kwargs): + pass + + self.stubs.Set(compute_utils, 'notify_about_instance_usage', dumb) + # set up our cloud + self.cloud = cloud.CloudController() + self.flags(compute_scheduler_driver='nova.scheduler.' + 'chance.ChanceScheduler') + + # set up services + self.compute = self.start_service('compute') + self.scheduler = self.start_service('scheduler') + self.network = self.start_service('network') + self.volume = self.start_service('volume') + + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.RequestContext(self.user_id, + self.project_id, + is_admin=True) + self.volume_api = volume.API() + + # NOTE(comstud): Make 'cast' behave like a 'call' which will + # ensure that operations complete + self.stubs.Set(rpc, 'cast', rpc.call) + + # make sure we can map ami-00000001/2 to a uuid in FakeImageService + db.api.s3_image_create(self.context, + 'cedef40a-ed67-4d10-800e-17455edce175') + db.api.s3_image_create(self.context, + '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6') + + def tearDown(self): + self.volume_api.reset_fake_api(self.context) + super(CinderCloudTestCase, self).tearDown() + fake.FakeImageService_reset() + + def _stub_instance_get_with_fixed_ips(self, func_name): + orig_func = getattr(self.cloud.compute_api, func_name) + + def fake_get(*args, **kwargs): + return get_instances_with_cached_ips(orig_func, *args, **kwargs) + self.stubs.Set(self.cloud.compute_api, func_name, fake_get) + + def _create_key(self, name): + # NOTE(vish): create depends on pool, so just call helper directly + keypair_api = compute_api.KeypairAPI() + return keypair_api.create_key_pair(self.context, self.context.user_id, + name) + + def test_describe_volumes(self): + """Makes sure describe_volumes works and filters results.""" + + vol1 = self.cloud.create_volume(self.context, + size=1, + name='test-1', + description='test volume 1') + vol2 = self.cloud.create_volume(self.context, + size=1, + name='test-2', + description='test volume 2') + result = self.cloud.describe_volumes(self.context) + self.assertEqual(len(result['volumeSet']), 2) + result = self.cloud.describe_volumes(self.context, + [vol1['volumeId']]) + self.assertEqual(len(result['volumeSet']), 1) + self.assertEqual(vol1['volumeId'], result['volumeSet'][0]['volumeId']) + + self.cloud.delete_volume(self.context, vol1['volumeId']) + self.cloud.delete_volume(self.context, vol2['volumeId']) + + def test_create_volume_in_availability_zone(self): + """Makes sure create_volume works when we specify an availability + zone + """ + availability_zone = 'zone1:host1' + + result = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + volume_id = result['volumeId'] + availabilityZone = result['availabilityZone'] + self.assertEqual(availabilityZone, availability_zone) + result = self.cloud.describe_volumes(self.context) + self.assertEqual(len(result['volumeSet']), 1) + self.assertEqual(result['volumeSet'][0]['volumeId'], volume_id) + self.assertEqual(result['volumeSet'][0]['availabilityZone'], + availabilityZone) + + self.cloud.delete_volume(self.context, volume_id) + + def test_create_volume_from_snapshot(self): + """Makes sure create_volume works when we specify a snapshot.""" + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + snap = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap of vol %s' + % vol1['volumeId']) + + vol2 = self.cloud.create_volume(self.context, + snapshot_id=snap['snapshotId']) + volume1_id = vol1['volumeId'] + volume2_id = vol2['volumeId'] + + result = self.cloud.describe_volumes(self.context) + self.assertEqual(len(result['volumeSet']), 2) + self.assertEqual(result['volumeSet'][1]['volumeId'], volume2_id) + + self.cloud.delete_volume(self.context, volume2_id) + self.cloud.delete_snapshot(self.context, snap['snapshotId']) + self.cloud.delete_volume(self.context, volume1_id) + + def test_describe_snapshots(self): + """Makes sure describe_snapshots works and filters results.""" + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + snap1 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap1 of vol %s' % + vol1['volumeId']) + snap2 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap2 of vol %s' % + vol1['volumeId']) + + result = self.cloud.describe_snapshots(self.context) + self.assertEqual(len(result['snapshotSet']), 2) + result = self.cloud.describe_snapshots( + self.context, + snapshot_id=[snap2['snapshotId']]) + self.assertEqual(len(result['snapshotSet']), 1) + + self.cloud.delete_snapshot(self.context, snap1['snapshotId']) + self.cloud.delete_snapshot(self.context, snap2['snapshotId']) + self.cloud.delete_volume(self.context, vol1['volumeId']) + + def test_create_snapshot(self): + """Makes sure create_snapshot works.""" + availability_zone = 'zone1:host1' + result = self.cloud.describe_snapshots(self.context) + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + snap1 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap1 of vol %s' % + vol1['volumeId']) + + snapshot_id = snap1['snapshotId'] + result = self.cloud.describe_snapshots(self.context) + self.assertEqual(len(result['snapshotSet']), 1) + self.assertEqual(result['snapshotSet'][0]['snapshotId'], snapshot_id) + + self.cloud.delete_snapshot(self.context, snap1['snapshotId']) + self.cloud.delete_volume(self.context, vol1['volumeId']) + + def test_delete_snapshot(self): + """Makes sure delete_snapshot works.""" + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + snap1 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap1 of vol %s' % + vol1['volumeId']) + + snapshot_id = snap1['snapshotId'] + result = self.cloud.delete_snapshot(self.context, + snapshot_id=snapshot_id) + self.assertTrue(result) + self.cloud.delete_volume(self.context, vol1['volumeId']) + + def _block_device_mapping_create(self, instance_uuid, mappings): + volumes = [] + for bdm in mappings: + db.block_device_mapping_create(self.context, bdm) + if 'volume_id' in bdm: + values = {'id': bdm['volume_id']} + for bdm_key, vol_key in [('snapshot_id', 'snapshot_id'), + ('snapshot_size', 'volume_size'), + ('delete_on_termination', + 'delete_on_termination')]: + if bdm_key in bdm: + values[vol_key] = bdm[bdm_key] + kwargs = {'name': 'bdmtest-volume', + 'description': 'bdm test volume description', + 'status': 'available', + 'host': self.volume.host, + 'size': 1, + 'attach_status': 'detached', + 'volume_id': values['id']} + vol = self.volume_api.create_with_kwargs(self.context, + **kwargs) + if 'snapshot_id' in values: + self.volume_api.create_snapshot(self.context, + vol, + 'snapshot-bdm', + 'fake snap for bdm tests', + values['snapshot_id']) + + self.volume_api.attach(self.context, vol, + instance_uuid, bdm['device_name']) + volumes.append(vol) + return volumes + + def _setUpBlockDeviceMapping(self): + image_uuid = 'cedef40a-ed67-4d10-800e-17455edce175' + inst1 = db.instance_create(self.context, + {'image_ref': image_uuid, + 'instance_type_id': 1, + 'root_device_name': '/dev/sdb1'}) + inst2 = db.instance_create(self.context, + {'image_ref': image_uuid, + 'instance_type_id': 1, + 'root_device_name': '/dev/sdc1'}) + + instance_uuid = inst1['uuid'] + mappings0 = [ + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb1', + 'snapshot_id': '1', + 'volume_id': '2'}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb2', + 'volume_id': '3', + 'volume_size': 1}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb3', + 'delete_on_termination': True, + 'snapshot_id': '4', + 'volume_id': '5'}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb4', + 'delete_on_termination': False, + 'snapshot_id': '6', + 'volume_id': '7'}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb5', + 'snapshot_id': '8', + 'volume_id': '9', + 'volume_size': 0}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb6', + 'snapshot_id': '10', + 'volume_id': '11', + 'volume_size': 1}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb7', + 'no_device': True}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb8', + 'virtual_name': 'swap'}, + {'instance_uuid': instance_uuid, + 'device_name': '/dev/sdb9', + 'virtual_name': 'ephemeral3'}] + + volumes = self._block_device_mapping_create(instance_uuid, mappings0) + return (inst1, inst2, volumes) + + def _tearDownBlockDeviceMapping(self, inst1, inst2, volumes): + for vol in volumes: + self.volume_api.delete(self.context, vol) + for uuid in (inst1['uuid'], inst2['uuid']): + for bdm in db.block_device_mapping_get_all_by_instance( + self.context, uuid): + db.block_device_mapping_destroy(self.context, bdm['id']) + db.instance_destroy(self.context, inst2['uuid']) + db.instance_destroy(self.context, inst1['uuid']) + + _expected_instance_bdm1 = { + 'instanceId': 'i-00000001', + 'rootDeviceName': '/dev/sdb1', + 'rootDeviceType': 'ebs'} + + _expected_block_device_mapping0 = [ + {'deviceName': '/dev/sdb1', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': False, + 'volumeId': '2', + }}, + {'deviceName': '/dev/sdb2', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': False, + 'volumeId': '3', + }}, + {'deviceName': '/dev/sdb3', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': True, + 'volumeId': '5', + }}, + {'deviceName': '/dev/sdb4', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': False, + 'volumeId': '7', + }}, + {'deviceName': '/dev/sdb5', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': False, + 'volumeId': '9', + }}, + {'deviceName': '/dev/sdb6', + 'ebs': {'status': 'in-use', + 'deleteOnTermination': False, + 'volumeId': '11', }}] + # NOTE(yamahata): swap/ephemeral device case isn't supported yet. + + _expected_instance_bdm2 = { + 'instanceId': 'i-00000002', + 'rootDeviceName': '/dev/sdc1', + 'rootDeviceType': 'instance-store'} + + def test_format_instance_bdm(self): + (inst1, inst2, volumes) = self._setUpBlockDeviceMapping() + + result = {} + self.cloud._format_instance_bdm(self.context, inst1['uuid'], + '/dev/sdb1', result) + self.assertSubDictMatch( + {'rootDeviceType': self._expected_instance_bdm1['rootDeviceType']}, + result) + self._assertEqualBlockDeviceMapping( + self._expected_block_device_mapping0, result['blockDeviceMapping']) + + result = {} + self.cloud._format_instance_bdm(self.context, inst2['uuid'], + '/dev/sdc1', result) + self.assertSubDictMatch( + {'rootDeviceType': self._expected_instance_bdm2['rootDeviceType']}, + result) + + self._tearDownBlockDeviceMapping(inst1, inst2, volumes) + + def _assertInstance(self, instance_id): + ec2_instance_id = ec2utils.id_to_ec2_id(instance_id) + result = self.cloud.describe_instances(self.context, + instance_id=[ec2_instance_id]) + result = result['reservationSet'][0] + self.assertEqual(len(result['instancesSet']), 1) + result = result['instancesSet'][0] + self.assertEqual(result['instanceId'], ec2_instance_id) + return result + + def _assertEqualBlockDeviceMapping(self, expected, result): + self.assertEqual(len(expected), len(result)) + for x in expected: + found = False + for y in result: + if x['deviceName'] == y['deviceName']: + self.assertSubDictMatch(x, y) + found = True + break + self.assertTrue(found) + + def test_describe_instances_bdm(self): + """Make sure describe_instances works with root_device_name and + block device mappings + """ + (inst1, inst2, volumes) = self._setUpBlockDeviceMapping() + + result = self._assertInstance(inst1['id']) + self.assertSubDictMatch(self._expected_instance_bdm1, result) + self._assertEqualBlockDeviceMapping( + self._expected_block_device_mapping0, result['blockDeviceMapping']) + + result = self._assertInstance(inst2['id']) + self.assertSubDictMatch(self._expected_instance_bdm2, result) + + self._tearDownBlockDeviceMapping(inst1, inst2, volumes) + + def assertDictListUnorderedMatch(self, L1, L2, key): + self.assertEqual(len(L1), len(L2)) + for d1 in L1: + self.assertTrue(key in d1) + for d2 in L2: + self.assertTrue(key in d2) + if d1[key] == d2[key]: + self.assertDictMatch(d1, d2) + + def _setUpImageSet(self, create_volumes_and_snapshots=False): + mappings1 = [ + {'device': '/dev/sda1', 'virtual': 'root'}, + + {'device': 'sdb0', 'virtual': 'ephemeral0'}, + {'device': 'sdb1', 'virtual': 'ephemeral1'}, + {'device': 'sdb2', 'virtual': 'ephemeral2'}, + {'device': 'sdb3', 'virtual': 'ephemeral3'}, + {'device': 'sdb4', 'virtual': 'ephemeral4'}, + + {'device': 'sdc0', 'virtual': 'swap'}, + {'device': 'sdc1', 'virtual': 'swap'}, + {'device': 'sdc2', 'virtual': 'swap'}, + {'device': 'sdc3', 'virtual': 'swap'}, + {'device': 'sdc4', 'virtual': 'swap'}] + block_device_mapping1 = [ + {'device_name': '/dev/sdb1', 'snapshot_id': 01234567}, + {'device_name': '/dev/sdb2', 'volume_id': 01234567}, + {'device_name': '/dev/sdb3', 'virtual_name': 'ephemeral5'}, + {'device_name': '/dev/sdb4', 'no_device': True}, + + {'device_name': '/dev/sdc1', 'snapshot_id': 12345678}, + {'device_name': '/dev/sdc2', 'volume_id': 12345678}, + {'device_name': '/dev/sdc3', 'virtual_name': 'ephemeral6'}, + {'device_name': '/dev/sdc4', 'no_device': True}] + image1 = { + 'id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'properties': { + 'kernel_id': 'cedef40a-ed67-4d10-800e-17455edce175', + 'type': 'machine', + 'image_state': 'available', + 'mappings': mappings1, + 'block_device_mapping': block_device_mapping1, + } + } + + mappings2 = [{'device': '/dev/sda1', 'virtual': 'root'}] + block_device_mapping2 = [{'device_name': '/dev/sdb1', + 'snapshot_id': 01234567}] + image2 = { + 'id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', + 'properties': { + 'kernel_id': '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6', + 'type': 'machine', + 'root_device_name': '/dev/sdb1', + 'mappings': mappings2, + 'block_device_mapping': block_device_mapping2}} + + def fake_show(meh, context, image_id): + _images = [copy.deepcopy(image1), copy.deepcopy(image2)] + for i in _images: + if str(i['id']) == str(image_id): + return i + raise exception.ImageNotFound(image_id=image_id) + + def fake_detail(meh, context): + return [copy.deepcopy(image1), copy.deepcopy(image2)] + + self.stubs.Set(fake._FakeImageService, 'show', fake_show) + self.stubs.Set(fake._FakeImageService, 'detail', fake_detail) + + volumes = [] + snapshots = [] + if create_volumes_and_snapshots: + for bdm in block_device_mapping1: + if 'volume_id' in bdm: + vol = self._volume_create(bdm['volume_id']) + volumes.append(vol['id']) + if 'snapshot_id' in bdm: + kwargs = {'volume_id': 76543210, + 'volume_size': 1, + 'name': 'test-snap', + 'description': 'test snap desc', + 'snap_id': bdm['snapshot_id'], + 'status': 'available'} + snap = self.volume_api.create_snapshot_with_kwargs( + self.context, **kwargs) + snapshots.append(snap['id']) + return (volumes, snapshots) + + def _assertImageSet(self, result, root_device_type, root_device_name): + self.assertEqual(1, len(result['imagesSet'])) + result = result['imagesSet'][0] + self.assertTrue('rootDeviceType' in result) + self.assertEqual(result['rootDeviceType'], root_device_type) + self.assertTrue('rootDeviceName' in result) + self.assertEqual(result['rootDeviceName'], root_device_name) + self.assertTrue('blockDeviceMapping' in result) + + return result + + _expected_root_device_name1 = '/dev/sda1' + # NOTE(yamahata): noDevice doesn't make sense when returning mapping + # It makes sense only when user overriding existing + # mapping. + _expected_bdms1 = [ + {'deviceName': '/dev/sdb0', 'virtualName': 'ephemeral0'}, + {'deviceName': '/dev/sdb1', 'ebs': {'snapshotId': + 'snap-00053977'}}, + {'deviceName': '/dev/sdb2', 'ebs': {'snapshotId': + 'vol-00053977'}}, + {'deviceName': '/dev/sdb3', 'virtualName': 'ephemeral5'}, + # {'deviceName': '/dev/sdb4', 'noDevice': True}, + + {'deviceName': '/dev/sdc0', 'virtualName': 'swap'}, + {'deviceName': '/dev/sdc1', 'ebs': {'snapshotId': + 'snap-00bc614e'}}, + {'deviceName': '/dev/sdc2', 'ebs': {'snapshotId': + 'vol-00bc614e'}}, + {'deviceName': '/dev/sdc3', 'virtualName': 'ephemeral6'}, + # {'deviceName': '/dev/sdc4', 'noDevice': True} + ] + + _expected_root_device_name2 = '/dev/sdb1' + _expected_bdms2 = [{'deviceName': '/dev/sdb1', + 'ebs': {'snapshotId': 'snap-00053977'}}] + + def _run_instance(self, **kwargs): + rv = self.cloud.run_instances(self.context, **kwargs) + instance_id = rv['instancesSet'][0]['instanceId'] + return instance_id + + def _restart_compute_service(self, periodic_interval=None): + """restart compute service. NOTE: fake driver forgets all instances.""" + self.compute.kill() + if periodic_interval: + self.compute = self.start_service( + 'compute', periodic_interval=periodic_interval) + else: + self.compute = self.start_service('compute') + + def _volume_create(self, volume_id=None): + kwargs = {'name': 'test-volume', + 'description': 'test volume description', + 'status': 'available', + 'host': self.volume.host, + 'size': 1, + 'attach_status': 'detached'} + if volume_id: + kwargs['volume_id'] = volume_id + return self.volume_api.create_with_kwargs(self.context, **kwargs) + #return db.volume_create(self.context, kwargs) + + def _assert_volume_attached(self, vol, instance_uuid, mountpoint): + self.assertEqual(vol['instance_uuid'], instance_uuid) + self.assertEqual(vol['mountpoint'], mountpoint) + self.assertEqual(vol['status'], "in-use") + self.assertEqual(vol['attach_status'], "attached") + + def _assert_volume_detached(self, vol): + self.assertEqual(vol['instance_uuid'], None) + self.assertEqual(vol['mountpoint'], None) + self.assertEqual(vol['status'], "available") + self.assertEqual(vol['attach_status'], "detached") + + def test_stop_start_with_volume(self): + """Make sure run instance with block device mapping works""" + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + vol2 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + vol1_uuid = ec2utils.ec2_vol_id_to_uuid(vol1['volumeId']) + vol2_uuid = ec2utils.ec2_vol_id_to_uuid(vol2['volumeId']) + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'volume_id': vol1_uuid, + 'delete_on_termination': False}, + {'device_name': '/dev/vdc', + 'volume_id': vol2_uuid, + 'delete_on_termination': True}, + ]} + ec2_instance_id = self._run_instance(**kwargs) + instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context, + ec2_instance_id) + vols = self.volume_api.get_all(self.context) + vols = [v for v in vols if v['instance_uuid'] == instance_uuid] + + self.assertEqual(len(vols), 2) + for vol in vols: + self.assertTrue(str(vol['id']) == str(vol1_uuid) or + str(vol['id']) == str(vol2_uuid)) + if(str(vol['id']) == str(vol1_uuid)): + self.volume_api.attach(self.context, vol, + instance_uuid, '/dev/vdb') + elif(str(vol['id']) == str(vol2_uuid)): + self.volume_api.attach(self.context, vol, + instance_uuid, '/dev/vdc') + + vol = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdb') + + vol = self.volume_api.get(self.context, vol2_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdc') + + result = self.cloud.stop_instances(self.context, [ec2_instance_id]) + self.assertTrue(result) + + vol = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdb') + + vol = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdb') + + vol = self.volume_api.get(self.context, vol2_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdc') + + self.cloud.start_instances(self.context, [ec2_instance_id]) + vols = self.volume_api.get_all(self.context) + vols = [v for v in vols if v['instance_uuid'] == instance_uuid] + self.assertEqual(len(vols), 2) + for vol in vols: + self.assertTrue(str(vol['id']) == str(vol1_uuid) or + str(vol['id']) == str(vol2_uuid)) + self.assertTrue(vol['mountpoint'] == '/dev/vdb' or + vol['mountpoint'] == '/dev/vdc') + self.assertEqual(vol['instance_uuid'], instance_uuid) + self.assertEqual(vol['status'], "in-use") + self.assertEqual(vol['attach_status'], "attached") + + #Here we puke... + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + + admin_ctxt = context.get_admin_context(read_deleted="no") + vol = self.volume_api.get(admin_ctxt, vol2_uuid) + self.assertFalse(vol['deleted']) + self.cloud.delete_volume(self.context, vol1['volumeId']) + self._restart_compute_service() + + def test_stop_with_attached_volume(self): + """Make sure attach info is reflected to block device mapping""" + + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + vol2 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + vol1_uuid = ec2utils.ec2_vol_id_to_uuid(vol1['volumeId']) + vol2_uuid = ec2utils.ec2_vol_id_to_uuid(vol2['volumeId']) + + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'volume_id': vol1_uuid, + 'delete_on_termination': True}]} + ec2_instance_id = self._run_instance(**kwargs) + instance_id = ec2utils.ec2_id_to_id(ec2_instance_id) + instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context, + ec2_instance_id) + + vols = self.volume_api.get_all(self.context) + vols = [v for v in vols if v['instance_uuid'] == instance_uuid] + self.assertEqual(len(vols), 1) + for vol in vols: + self.assertEqual(vol['id'], vol1_uuid) + self._assert_volume_attached(vol, instance_uuid, '/dev/vdb') + vol = self.volume_api.get(self.context, vol2_uuid) + self._assert_volume_detached(vol) + + instance = db.instance_get(self.context, instance_id) + self.cloud.compute_api.attach_volume(self.context, + instance, + volume_id=vol2_uuid, + device='/dev/vdc') + + vol1 = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_attached(vol1, instance_uuid, '/dev/vdb') + + vol2 = self.volume_api.get(self.context, vol2_uuid) + self._assert_volume_attached(vol2, instance_uuid, '/dev/vdc') + + self.cloud.compute_api.detach_volume(self.context, + volume_id=vol1_uuid) + + vol1 = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_detached(vol1) + + result = self.cloud.stop_instances(self.context, [ec2_instance_id]) + self.assertTrue(result) + + vol2 = self.volume_api.get(self.context, vol2_uuid) + self._assert_volume_attached(vol2, instance_uuid, '/dev/vdc') + + self.cloud.start_instances(self.context, [ec2_instance_id]) + vols = self.volume_api.get_all(self.context) + vols = [v for v in vols if v['instance_uuid'] == instance_uuid] + self.assertEqual(len(vols), 1) + + self._assert_volume_detached(vol1) + + vol1 = self.volume_api.get(self.context, vol1_uuid) + self._assert_volume_detached(vol1) + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + + def _create_snapshot(self, ec2_volume_id): + result = self.cloud.create_snapshot(self.context, + volume_id=ec2_volume_id) + return result['snapshotId'] + + def test_run_with_snapshot(self): + """Makes sure run/stop/start instance with snapshot works.""" + availability_zone = 'zone1:host1' + vol1 = self.cloud.create_volume(self.context, + size=1, + availability_zone=availability_zone) + + snap1 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-1', + description='test snap of vol %s' % + vol1['volumeId']) + snap1_uuid = ec2utils.ec2_snap_id_to_uuid(snap1['snapshotId']) + + snap2 = self.cloud.create_snapshot(self.context, + vol1['volumeId'], + name='snap-2', + description='test snap of vol %s' % + vol1['volumeId']) + snap2_uuid = ec2utils.ec2_snap_id_to_uuid(snap2['snapshotId']) + + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1, + 'block_device_mapping': [{'device_name': '/dev/vdb', + 'snapshot_id': snap1_uuid, + 'delete_on_termination': False, }, + {'device_name': '/dev/vdc', + 'snapshot_id': snap2_uuid, + 'delete_on_termination': True}]} + ec2_instance_id = self._run_instance(**kwargs) + instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context, + ec2_instance_id) + + vols = self.volume_api.get_all(self.context) + vols = [v for v in vols if v['instance_uuid'] == instance_uuid] + + self.assertEqual(len(vols), 2) + + vol1_id = None + vol2_id = None + for vol in vols: + snapshot_uuid = vol['snapshot_id'] + if snapshot_uuid == snap1_uuid: + vol1_id = vol['id'] + mountpoint = '/dev/vdb' + elif snapshot_uuid == snap2_uuid: + vol2_id = vol['id'] + mountpoint = '/dev/vdc' + else: + self.fail() + + self._assert_volume_attached(vol, instance_uuid, mountpoint) + + #Just make sure we found them + self.assertTrue(vol1_id) + self.assertTrue(vol2_id) + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + + admin_ctxt = context.get_admin_context(read_deleted="no") + vol = self.volume_api.get(admin_ctxt, vol1_id) + self._assert_volume_detached(vol) + self.assertFalse(vol['deleted']) + #db.volume_destroy(self.context, vol1_id) + + ##admin_ctxt = context.get_admin_context(read_deleted="only") + ##vol = db.volume_get(admin_ctxt, vol2_id) + ##self.assertTrue(vol['deleted']) + + #for snapshot_id in (ec2_snapshot1_id, ec2_snapshot2_id): + # self.cloud.delete_snapshot(self.context, snapshot_id) + + def test_create_image(self): + """Make sure that CreateImage works""" + # enforce periodic tasks run in short time to avoid wait for 60s. + self._restart_compute_service(periodic_interval=0.3) + + (volumes, snapshots) = self._setUpImageSet( + create_volumes_and_snapshots=True) + + kwargs = {'image_id': 'ami-1', + 'instance_type': FLAGS.default_instance_type, + 'max_count': 1} + ec2_instance_id = self._run_instance(**kwargs) + + self.cloud.terminate_instances(self.context, [ec2_instance_id]) + self._restart_compute_service() + + @staticmethod + def _fake_bdm_get(ctxt, id): + return [{'volume_id': 87654321, + 'snapshot_id': None, + 'no_device': None, + 'virtual_name': None, + 'delete_on_termination': True, + 'device_name': '/dev/sdh'}, + {'volume_id': None, + 'snapshot_id': 98765432, + 'no_device': None, + 'virtual_name': None, + 'delete_on_termination': True, + 'device_name': '/dev/sdi'}, + {'volume_id': None, + 'snapshot_id': None, + 'no_device': True, + 'virtual_name': None, + 'delete_on_termination': None, + 'device_name': None}, + {'volume_id': None, + 'snapshot_id': None, + 'no_device': None, + 'virtual_name': 'ephemeral0', + 'delete_on_termination': None, + 'device_name': '/dev/sdb'}, + {'volume_id': None, + 'snapshot_id': None, + 'no_device': None, + 'virtual_name': 'swap', + 'delete_on_termination': None, + 'device_name': '/dev/sdc'}, + {'volume_id': None, + 'snapshot_id': None, + 'no_device': None, + 'virtual_name': 'ephemeral1', + 'delete_on_termination': None, + 'device_name': '/dev/sdd'}, + {'volume_id': None, + 'snapshot_id': None, + 'no_device': None, + 'virtual_name': 'ephemeral2', + 'delete_on_termination': None, + 'device_name': '/dev/sd3'}, + ] diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index 0603cac62..7ac9cd862 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -37,8 +37,8 @@ from nova import db from nova import exception from nova import flags from nova.image import s3 -from nova import log as logging from nova.network import api as network_api +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import test from nova.tests.image import fake @@ -1018,7 +1018,6 @@ class CloudTestCase(test.TestCase): db.instance_destroy(self.context, inst2['uuid']) db.instance_destroy(self.context, inst1['uuid']) - # NOTE(jdg) Modified expected volume_id's to string _expected_instance_bdm1 = { 'instanceId': 'i-00000001', 'rootDeviceName': '/dev/sdb1', diff --git a/nova/tests/api/ec2/test_ec2_validate.py b/nova/tests/api/ec2/test_ec2_validate.py index f9c1d2be8..18f99ffbb 100644 --- a/nova/tests/api/ec2/test_ec2_validate.py +++ b/nova/tests/api/ec2/test_ec2_validate.py @@ -22,8 +22,8 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova import test from nova.tests.image import fake diff --git a/nova/tests/api/openstack/compute/contrib/test_aggregates.py b/nova/tests/api/openstack/compute/contrib/test_aggregates.py index fc19a9545..c2c5a1486 100644 --- a/nova/tests/api/openstack/compute/contrib/test_aggregates.py +++ b/nova/tests/api/openstack/compute/contrib/test_aggregates.py @@ -20,7 +20,7 @@ from webob import exc from nova.api.openstack.compute.contrib import aggregates from nova import context from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import test diff --git a/nova/tests/api/openstack/compute/contrib/test_hosts.py b/nova/tests/api/openstack/compute/contrib/test_hosts.py index 40805bb66..686548726 100644 --- a/nova/tests/api/openstack/compute/contrib/test_hosts.py +++ b/nova/tests/api/openstack/compute/contrib/test_hosts.py @@ -23,7 +23,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.openstack.common import log as logging from nova import test diff --git a/nova/tests/api/openstack/compute/contrib/test_snapshots.py b/nova/tests/api/openstack/compute/contrib/test_snapshots.py index 365c88ef8..b1d05118d 100644 --- a/nova/tests/api/openstack/compute/contrib/test_snapshots.py +++ b/nova/tests/api/openstack/compute/contrib/test_snapshots.py @@ -20,8 +20,8 @@ from nova.api.openstack.compute.contrib import volumes from nova import context from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/compute/contrib/test_volume_types.py b/nova/tests/api/openstack/compute/contrib/test_volume_types.py index 3d0254790..4ad6297b8 100644 --- a/nova/tests/api/openstack/compute/contrib/test_volume_types.py +++ b/nova/tests/api/openstack/compute/contrib/test_volume_types.py @@ -18,7 +18,7 @@ import webob from nova.api.openstack.compute.contrib import volumetypes from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.tests.api.openstack import fakes from nova.volume import volume_types diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py index ca5efe6c3..5b9fdc3d2 100644 --- a/nova/tests/api/openstack/compute/test_servers.py +++ b/nova/tests/api/openstack/compute/test_servers.py @@ -119,6 +119,12 @@ class ServersControllerTest(test.TestCase): fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs, spectacular=True) + def test_requested_networks_prefix(self): + uuid = 'br-00000000-0000-0000-0000-000000000000' + requested_networks = [{'uuid': uuid}] + res = self.controller._get_requested_networks(requested_networks) + self.assertTrue((uuid, None) in res) + def test_get_server_by_uuid(self): req = fakes.HTTPRequest.blank('/v2/fake/servers/%s' % FAKE_UUID) res_dict = self.controller.show(req, FAKE_UUID) diff --git a/nova/tests/api/openstack/compute/test_urlmap.py b/nova/tests/api/openstack/compute/test_urlmap.py index 780879e3c..c0b20bbeb 100644 --- a/nova/tests/api/openstack/compute/test_urlmap.py +++ b/nova/tests/api/openstack/compute/test_urlmap.py @@ -15,8 +15,8 @@ import webob -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import test from nova.tests.api.openstack import fakes import nova.tests.image.fake diff --git a/nova/tests/api/openstack/volume/test_router.py b/nova/tests/api/openstack/volume/test_router.py index 53d96c191..677cddb6a 100644 --- a/nova/tests/api/openstack/volume/test_router.py +++ b/nova/tests/api/openstack/volume/test_router.py @@ -20,7 +20,7 @@ from nova.api.openstack.volume import versions from nova.api.openstack.volume import volumes from nova.api.openstack import wsgi from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/openstack/volume/test_snapshots.py b/nova/tests/api/openstack/volume/test_snapshots.py index 35708685a..ccd3a70b4 100644 --- a/nova/tests/api/openstack/volume/test_snapshots.py +++ b/nova/tests/api/openstack/volume/test_snapshots.py @@ -19,7 +19,7 @@ import webob from nova.api.openstack.volume import snapshots from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import test from nova.tests.api.openstack import fakes diff --git a/nova/tests/api/test_auth.py b/nova/tests/api/test_auth.py index 10d2cecbf..e937da541 100644 --- a/nova/tests/api/test_auth.py +++ b/nova/tests/api/test_auth.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import json import webob import nova.api.auth @@ -33,6 +34,7 @@ class TestNovaKeystoneContextMiddleware(test.TestCase): self.request = webob.Request.blank('/') self.request.headers['X_TENANT_ID'] = 'testtenantid' self.request.headers['X_AUTH_TOKEN'] = 'testauthtoken' + self.request.headers['X_SERVICE_CATALOG'] = json.dumps({}) def test_no_user_or_user_id(self): response = self.request.get_response(self.middleware) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index a7307cd1c..784054d0e 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -39,9 +39,9 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.notifier import test_notifier from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import policy as common_policy from nova.openstack.common import rpc from nova.openstack.common.rpc import common as rpc_common diff --git a/nova/tests/consoleauth/test_consoleauth.py b/nova/tests/consoleauth/test_consoleauth.py index 03cd157da..da50eb83b 100644 --- a/nova/tests/consoleauth/test_consoleauth.py +++ b/nova/tests/consoleauth/test_consoleauth.py @@ -25,7 +25,7 @@ import time from nova.consoleauth import manager from nova import context from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import test diff --git a/nova/tests/fake_libvirt_utils.py b/nova/tests/fake_libvirt_utils.py index 2cd5bb75e..1467a26de 100644 --- a/nova/tests/fake_libvirt_utils.py +++ b/nova/tests/fake_libvirt_utils.py @@ -118,11 +118,6 @@ def file_delete(path): return True -def get_open_port(start_port, end_port): - # Return the port in the middle - return int((start_port + end_port) / 2) - - def get_fs_info(path): return {'total': 128 * (1024 ** 3), 'used': 44 * (1024 ** 3), diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py index e64ff97a1..08a84e73f 100644 --- a/nova/tests/fake_utils.py +++ b/nova/tests/fake_utils.py @@ -21,7 +21,7 @@ import re from eventlet import greenthread from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils LOG = logging.getLogger(__name__) diff --git a/nova/tests/fake_volume.py b/nova/tests/fake_volume.py new file mode 100644 index 000000000..040897582 --- /dev/null +++ b/nova/tests/fake_volume.py @@ -0,0 +1,260 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# 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. + +"""Implementation of a fake volume API""" + +from nova import exception +from nova.openstack.common import log as logging +from nova.openstack.common import timeutils +from nova import utils + +LOG = logging.getLogger(__name__) + + +class fake_volume(): + user_uuid = '4a3cd440-b9c2-11e1-afa6-0800200c9a66' + instance_uuid = '4a3cd441-b9c2-11e1-afa6-0800200c9a66' + + def __init__(self, size, name, + description, id, snapshot, + volume_type, metadata, + availability_zone): + snapshot_id = None + if snapshot is not None: + snapshot_id = snapshot['id'] + if id is None: + id = str(utils.gen_uuid()) + self.vol = { + 'created_at': timeutils.utcnow(), + 'deleted_at': None, + 'updated_at': timeutils.utcnow(), + 'uuid': 'WTF', + 'deleted': False, + 'id': id, + 'user_id': self.user_uuid, + 'project_id': 'fake-project-id', + 'snapshot_id': snapshot_id, + 'host': None, + 'size': size, + 'availability_zone': availability_zone, + 'instance_uuid': None, + 'mountpoint': None, + 'attach_time': timeutils.utcnow(), + 'status': 'available', + 'attach_status': 'detached', + 'scheduled_at': None, + 'launched_at': None, + 'terminated_at': None, + 'display_name': name, + 'display_description': description, + 'provider_location': 'fake-location', + 'provider_auth': 'fake-auth', + 'volume_type_id': 99 + } + + def get(self, key, default=None): + return self.vol[key] + + def __setitem__(self, key, value): + self.vol[key] = value + + def __getitem__(self, key): + self.vol[key] + + +class fake_snapshot(): + user_uuid = '4a3cd440-b9c2-11e1-afa6-0800200c9a66' + instance_uuid = '4a3cd441-b9c2-11e1-afa6-0800200c9a66' + + def __init__(self, volume_id, size, name, desc, id=None): + if id is None: + id = str(utils.gen_uuid()) + self.snap = { + 'created_at': timeutils.utcnow(), + 'deleted_at': None, + 'updated_at': timeutils.utcnow(), + 'uuid': 'WTF', + 'deleted': False, + 'id': str(id), + 'volume_id': volume_id, + 'status': 'creating', + 'progress': '0%', + 'volume_size': 1, + 'display_name': name, + 'display_description': desc, + 'user_id': self.user_uuid, + 'project_id': 'fake-project-id' + } + + def get(self, key, default=None): + return self.snap[key] + + def __setitem__(self, key, value): + self.snap[key] = value + + def __getitem__(self, key): + self.snap[key] + + +class API(object): + volume_list = [] + snapshot_list = [] + _instance = None + + class Singleton: + def __init__(self): + self.API = None + + def __init__(self): + if API._instance is None: + API._instance = API.Singleton() + + self._EventHandler_instance = API._instance + + def create(self, context, size, name, description, snapshot=None, + volume_type=None, metadata=None, availability_zone=None): + v = fake_volume(size, name, + description, None, + snapshot, volume_type, + metadata, availability_zone) + self.volume_list.append(v.vol) + LOG.info('creating volume %s', v.vol['id']) + return v.vol + + def create_with_kwargs(self, context, **kwargs): + v = fake_volume(kwargs['size'], + kwargs['name'], + kwargs['description'], + str(kwargs.get('volume_id', None)), + None, + None, + None, + None) + if kwargs.get('status', None) is not None: + v.vol['status'] = kwargs['status'] + if kwargs['host'] is not None: + v.vol['host'] = kwargs['host'] + if kwargs['attach_status'] is not None: + v.vol['attach_status'] = kwargs['attach_status'] + if kwargs.get('snapshot_id', None) is not None: + v.vol['snapshot_id'] = kwargs['snapshot_id'] + + self.volume_list.append(v.vol) + return v.vol + + def get(self, context, volume_id): + if volume_id == 87654321: + return {'id': volume_id, + 'attach_time': '13:56:24', + 'status': 'in-use'} + + for v in self.volume_list: + if v['id'] == str(volume_id): + return v + + def get_all(self, context): + return self.volume_list + + def delete(self, context, volume): + LOG.info('deleting volume %s', volume['id']) + self.volume_list = [v for v in self.volume_list if v != volume] + + def check_attach(self, context, volume): + if volume['status'] != 'available': + msg = _("status must be available") + raise exception.InvalidVolume(reason=msg) + if volume['attach_status'] == 'attached': + msg = _("already attached") + raise exception.InvalidVolume(reason=msg) + + def check_detach(self, context, volume): + if volume['status'] == "available": + msg = _("already detached") + raise exception.InvalidVolume(reason=msg) + + def attach(self, context, volume, instance_uuid, mountpoint): + LOG.info('attaching volume %s', volume['id']) + volume = self.get(context, volume['id']) + volume['status'] = 'in-use' + volume['mountpoint'] = mountpoint + volume['attach_status'] = 'attached' + volume['instance_uuid'] = instance_uuid + volume['attach_time'] = timeutils.utcnow() + + def fake_set_snapshot_id(self, context, volume, snapshot_id): + volume['snapshot_id'] = snapshot_id + + def reset_fake_api(self, context): + del self.volume_list[:] + del self.snapshot_list[:] + + def detach(self, context, volume): + LOG.info('detaching volume %s', volume['id']) + volume = self.get(context, volume['id']) + volume['status'] = 'available' + volume['mountpoint'] = None + volume['attach_status'] = 'detached' + volume['instance_uuid'] = None + + def initialize_connection(self, context, volume_id, connector): + return {'driver_volume_type': 'iscsi', 'data': {}} + + def terminate_connection(self, context, volume_id, connector): + return None + + def get_snapshot(self, context, snapshot_id): + for snap in self.snapshot_list: + if snap['id'] == str(snapshot_id): + return snap + + def get_all_snapshots(self, context): + return self.snapshot_list + + def create_snapshot(self, context, volume, name, description, id=None): + snapshot = fake_snapshot(volume['id'], volume['size'], + name, description, id) + self.snapshot_list.append(snapshot.snap) + return snapshot.snap + + def create_snapshot_with_kwargs(self, context, **kwargs): + snapshot = fake_snapshot(kwargs.get('volume_id'), + kwargs.get('volume_size'), + kwargs.get('name'), + kwargs.get('description'), + kwargs.get('snap_id')) + + status = kwargs.get('status', None) + snapshot.snap['status'] = status + self.snapshot_list.append(snapshot.snap) + return snapshot.snap + + def create_snapshot_force(self, context, volume, + name, description, id=None): + snapshot = fake_snapshot(volume['id'], volume['size'], + name, description, id) + self.snapshot_list.append(snapshot.snap) + return snapshot.snap + + def delete_snapshot(self, context, snapshot): + self.snapshot_list = [s for s in self.snapshot_list if s != snapshot] + + def reserve_volume(self, context, volume): + LOG.info('reserving volume %s', volume['id']) + volume = self.get(context, volume['id']) + volume['status'] = 'attaching' + + def unreserve_volume(self, context, volume): + LOG.info('unreserving volume %s', volume['id']) + volume = self.get(context, volume['id']) + volume['status'] = 'available' diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py index 4350a8878..9349d6265 100644 --- a/nova/tests/fakelibvirt.py +++ b/nova/tests/fakelibvirt.py @@ -68,6 +68,8 @@ VIR_DOMAIN_SHUTDOWN = 4 VIR_DOMAIN_SHUTOFF = 5 VIR_DOMAIN_CRASHED = 6 +VIR_DOMAIN_XML_SECURE = 1 + VIR_CPU_COMPARE_ERROR = -1 VIR_CPU_COMPARE_INCOMPATIBLE = 0 VIR_CPU_COMPARE_IDENTICAL = 1 @@ -478,6 +480,9 @@ class Connection(object): node_cores, node_threads] + def numOfDomains(self): + return len(self._running_vms) + def listDomainsID(self): return self._running_vms.keys() diff --git a/nova/tests/image/fake.py b/nova/tests/image/fake.py index 99b1a6175..66e37e5d5 100644 --- a/nova/tests/image/fake.py +++ b/nova/tests/image/fake.py @@ -24,7 +24,7 @@ import datetime from nova import exception from nova import flags import nova.image.glance -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py index ce0dc0f82..36a90883b 100644 --- a/nova/tests/integrated/api/client.py +++ b/nova/tests/integrated/api/client.py @@ -18,8 +18,8 @@ import httplib import urllib import urlparse -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py index a514de677..8cd37ba6d 100644 --- a/nova/tests/integrated/integrated_helpers.py +++ b/nova/tests/integrated/integrated_helpers.py @@ -22,7 +22,8 @@ Provides common functionality for integrated unit tests import random import string -from nova.log import logging +import nova.image.glance +from nova.openstack.common.log import logging from nova import service from nova import test # For the flags import nova.tests.image.fake diff --git a/nova/tests/integrated/test_extensions.py b/nova/tests/integrated/test_extensions.py index e5b35645c..056ff32b7 100644 --- a/nova/tests/integrated/test_extensions.py +++ b/nova/tests/integrated/test_extensions.py @@ -16,7 +16,7 @@ # under the License. from nova import flags -from nova.log import logging +from nova.openstack.common.log import logging from nova.tests.integrated import integrated_helpers diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py index aa256f803..cecfef31a 100644 --- a/nova/tests/integrated/test_login.py +++ b/nova/tests/integrated/test_login.py @@ -16,7 +16,7 @@ # under the License. -from nova.log import logging +from nova.openstack.common.log import logging from nova.tests.integrated import integrated_helpers diff --git a/nova/tests/integrated/test_multiprocess_api.py b/nova/tests/integrated/test_multiprocess_api.py index 6fe1479cd..fbab7eb49 100644 --- a/nova/tests/integrated/test_multiprocess_api.py +++ b/nova/tests/integrated/test_multiprocess_api.py @@ -22,7 +22,7 @@ import time import traceback from nova import flags -from nova.log import logging +from nova.openstack.common.log import logging from nova import service from nova.tests.integrated import integrated_helpers diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index dccc879a7..49abd0011 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -18,7 +18,7 @@ import time import unittest -from nova.log import logging +from nova.openstack.common.log import logging from nova.tests.integrated.api import client from nova.tests.integrated import integrated_helpers import nova.virt.fake diff --git a/nova/tests/integrated/test_volumes.py b/nova/tests/integrated/test_volumes.py index 24bc43462..fe70c3ce8 100644 --- a/nova/tests/integrated/test_volumes.py +++ b/nova/tests/integrated/test_volumes.py @@ -18,7 +18,7 @@ import time import unittest -from nova.log import logging +from nova.openstack.common.log import logging from nova import service from nova.tests.integrated.api import client from nova.tests.integrated import integrated_helpers diff --git a/nova/tests/integrated/test_xml.py b/nova/tests/integrated/test_xml.py index 58ca4f726..b6bf197d7 100644 --- a/nova/tests/integrated/test_xml.py +++ b/nova/tests/integrated/test_xml.py @@ -19,7 +19,7 @@ from lxml import etree from nova.api.openstack import common from nova.api.openstack import xmlutil -from nova.log import logging +from nova.openstack.common.log import logging from nova.tests.integrated import integrated_helpers diff --git a/nova/tests/network/test_linux_net.py b/nova/tests/network/test_linux_net.py index 0660bb0f3..b05127c72 100644 --- a/nova/tests/network/test_linux_net.py +++ b/nova/tests/network/test_linux_net.py @@ -22,9 +22,9 @@ import mox from nova import context from nova import db from nova import flags -from nova import log as logging from nova.network import linux_net from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import test from nova import utils diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py index 11e293291..b3a5ef0ea 100644 --- a/nova/tests/network/test_manager.py +++ b/nova/tests/network/test_manager.py @@ -23,10 +23,10 @@ import tempfile from nova import context from nova import db from nova import exception -from nova import log as logging from nova.network import linux_net from nova.network import manager as network_manager from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc import nova.policy from nova import test @@ -943,6 +943,42 @@ class VlanNetworkTestCase(test.TestCase): fixed = db.fixed_ip_get_by_address(elevated, fix_addr) self.assertFalse(fixed['allocated']) + def test_deallocate_fixed_deleted(self): + """Verify doesn't deallocate deleted fixed_ip from deleted network""" + + def network_get(_context, network_id): + return networks[network_id] + + def teardown_network_on_host(_context, network): + if network['id'] == 0: + raise test.TestingException() + + self.stubs.Set(db, 'network_get', network_get) + self.stubs.Set(self.network, '_teardown_network_on_host', + teardown_network_on_host) + + context1 = context.RequestContext('user', 'project1') + + instance = db.instance_create(context1, + {'project_id': 'project1'}) + + elevated = context1.elevated() + fix_addr = db.fixed_ip_associate_pool(elevated, 1, instance['id']) + db.fixed_ip_update(elevated, fix_addr, {'deleted': 1}) + elevated.read_deleted = 'yes' + delfixed = db.fixed_ip_get_by_address(elevated, fix_addr) + values = {'address': fix_addr, + 'network_id': 0, + 'instance_id': delfixed['instance_id']} + db.fixed_ip_create(elevated, values) + elevated.read_deleted = 'no' + newfixed = db.fixed_ip_get_by_address(elevated, fix_addr) + elevated.read_deleted = 'yes' + + deallocate = self.network.deallocate_fixed_ip + self.assertRaises(test.TestingException, deallocate, context1, + fix_addr, 'fake') + def test_deallocate_fixed_no_vif(self): """Verify that deallocate doesn't raise when no vif is returned. diff --git a/nova/tests/network/test_network_info.py b/nova/tests/network/test_network_info.py index f1c908b2c..c9b17306d 100644 --- a/nova/tests/network/test_network_info.py +++ b/nova/tests/network/test_network_info.py @@ -16,8 +16,8 @@ # under the License. from nova import exception -from nova import log as logging from nova.network import model +from nova.openstack.common import log as logging from nova import test from nova.tests import fake_network_cache_model diff --git a/nova/tests/network/test_quantum.py b/nova/tests/network/test_quantum.py index 7f93c9c2b..5299bde93 100644 --- a/nova/tests/network/test_quantum.py +++ b/nova/tests/network/test_quantum.py @@ -23,13 +23,13 @@ from nova.db.sqlalchemy import models from nova.db.sqlalchemy import session as sql_session from nova import exception from nova import flags -from nova import log as logging from nova.network.quantum import client as quantum_client from nova.network.quantum import fake_client from nova.network.quantum import manager as quantum_manager from nova.network.quantum import melange_connection from nova.network.quantum import melange_ipam_lib from nova.network.quantum import quantum_connection +from nova.openstack.common import log as logging from nova import test from nova import utils @@ -291,7 +291,7 @@ class QuantumDeallocationTestCase(QuantumNovaTestCase): self.net_man.deallocate_ip_address('context', 'net_id', 'project_id', {'uuid': 1}, 'instance_id') - def test_deallocate_ip_address(self): + def test_deallocate_ip_address_2(self): ipam = self.mox.CreateMock(melange_ipam_lib.QuantumMelangeIPAMLib) ipam.get_tenant_id_by_net_id('context', 'net_id', {'uuid': 1}, 'project_id').AndRaise(Exception()) @@ -492,7 +492,7 @@ class QuantumManagerTestCase(QuantumNovaTestCase): net = db.network_get_by_uuid(ctx.elevated(), net_id) self.assertTrue(net is not None) self.assertEquals(net['uuid'], net_id) - self.assertTrue(net['host'] is not None) + self.assertTrue(net['host'] != None) class QuantumNovaMACGenerationTestCase(QuantumNovaTestCase): diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py new file mode 100644 index 000000000..7a12914cb --- /dev/null +++ b/nova/tests/network/test_quantumv2.py @@ -0,0 +1,411 @@ +# Copyright 2012 OpenStack LLC. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import mox + +from nova import context +from nova import exception +from nova.network import model +from nova.network import quantumv2 +from nova.network.quantumv2 import api as quantumapi +from nova.openstack.common import cfg +from nova import test +from nova import utils +from quantumclient.v2_0 import client + +FLAGS = cfg.CONF +#NOTE: Quantum client raises Exception which is discouraged by HACKING. +# We set this variable here and use it for assertions below to avoid +# the hacking checks until we can make quantum client throw a custom +# exception class instead. +QUANTUM_CLIENT_EXCEPTION = Exception + + +class MyComparator(mox.Comparator): + def __init__(self, lhs): + self.lhs = lhs + + def _com_dict(self, lhs, rhs): + if len(lhs) != len(rhs): + return False + for key, value in lhs.iteritems(): + if key not in rhs: + return False + rhs_value = rhs[key] + if not self._com(value, rhs_value): + return False + return True + + def _com_list(self, lhs, rhs): + if len(lhs) != len(rhs): + return False + for lhs_value in lhs: + if lhs_value not in rhs: + return False + return True + + def _com(self, lhs, rhs): + if lhs is None: + return rhs is None + if isinstance(lhs, dict): + if not isinstance(rhs, dict): + return False + return self._com_dict(lhs, rhs) + if isinstance(lhs, list): + if not isinstance(rhs, list): + return False + return self._com_list(lhs, rhs) + if isinstance(lhs, tuple): + if not isinstance(rhs, tuple): + return False + return self._com_list(lhs, rhs) + return lhs == rhs + + def equals(self, rhs): + return self._com(self.lhs, rhs) + + def __repr__(self): + return str(self.lhs) + + +class TestQuantumClient(test.TestCase): + def setUp(self): + super(TestQuantumClient, self).setUp() + + def test_withtoken(self): + self.flags(quantum_url='http://anyhost/') + self.flags(quantum_url_timeout=30) + my_context = context.RequestContext('userid', + 'my_tenantid', + auth_token='token') + self.mox.StubOutWithMock(client.Client, "__init__") + client.Client.__init__( + endpoint_url=FLAGS.quantum_url, + token=my_context.auth_token, + timeout=FLAGS.quantum_url_timeout).AndReturn(None) + self.mox.ReplayAll() + quantumv2.get_client(my_context) + + def test_withouttoken_keystone_connection_error(self): + self.flags(quantum_auth_strategy='keystone') + self.flags(quantum_url='http://anyhost/') + my_context = context.RequestContext('userid', 'my_tenantid') + self.assertRaises(QUANTUM_CLIENT_EXCEPTION, + quantumv2.get_client, + my_context) + + def test_withouttoken_keystone_not_auth(self): + # self.flags(quantum_auth_strategy=None) fail to work + old_quantum_auth_strategy = FLAGS.quantum_auth_strategy + setattr(FLAGS, 'quantum_auth_strategy', None) + self.flags(quantum_url='http://anyhost/') + self.flags(quantum_url_timeout=30) + my_context = context.RequestContext('userid', 'my_tenantid') + self.mox.StubOutWithMock(client.Client, "__init__") + client.Client.__init__( + endpoint_url=FLAGS.quantum_url, + auth_strategy=None, + timeout=FLAGS.quantum_url_timeout).AndReturn(None) + self.mox.ReplayAll() + try: + quantumv2.get_client(my_context) + finally: + setattr(FLAGS, 'quantum_auth_strategy', + old_quantum_auth_strategy) + + +class TestQuantumv2(test.TestCase): + + def setUp(self): + super(TestQuantumv2, self).setUp() + self.mox.StubOutWithMock(quantumv2, 'get_client') + self.moxed_client = self.mox.CreateMock(client.Client) + quantumv2.get_client(mox.IgnoreArg()).MultipleTimes().AndReturn( + self.moxed_client) + self.context = context.RequestContext('userid', 'my_tenantid') + setattr(self.context, + 'auth_token', + 'bff4a5a6b9eb4ea2a6efec6eefb77936') + self.instance = {'project_id': '9d049e4b60b64716978ab415e6fbd5c0', + 'uuid': str(utils.gen_uuid()), + 'display_name': 'test_instance'} + self.nets1 = [{'id': 'my_netid1', + 'name': 'my_netname1', + 'tenant_id': 'my_tenantid'}] + self.nets2 = [] + self.nets2.append(self.nets1[0]) + self.nets2.append({'id': 'my_netid2', + 'name': 'my_netname2', + 'tenant_id': 'my_tenantid'}) + + self.port_data1 = [{'network_id': 'my_netid1', + 'device_id': 'device_id1', + 'id': 'my_portid1', + 'fixed_ips': [{'ip_address': '10.0.1.2', + 'subnet_id': 'my_subid1'}], + 'mac_address': 'my_mac1', }] + self.port_data2 = [] + self.port_data2.append(self.port_data1[0]) + self.port_data2.append({'network_id': 'my_netid2', + 'device_id': 'device_id2', + 'id': 'my_portid2', + 'fixed_ips': [{'ip_address': '10.0.2.2', + 'subnet_id': 'my_subid2'}], + 'mac_address': 'my_mac2', }) + self.subnet_data1 = [{'cidr': '10.0.1.0/24', + 'gateway_ip': '10.0.1.1', + 'dns_nameservers': ['8.8.1.1', '8.8.1.2']}] + self.subnet_data2 = [] + self.subnet_data2.append({'cidr': '10.0.2.0/24', + 'gateway_ip': '10.0.2.1', + 'dns_nameservers': ['8.8.2.1', '8.8.2.2']}) + + def tearDown(self): + try: + self.mox.UnsetStubs() + self.mox.VerifyAll() + finally: + FLAGS.reset() + + def _verify_nw_info(self, nw_inf, index=0): + id_suffix = index + 1 + self.assertEquals('10.0.%s.2' % id_suffix, + nw_inf.fixed_ips()[index]['address']) + self.assertEquals('my_netname%s' % id_suffix, + nw_inf[index]['network']['label']) + self.assertEquals('my_portid%s' % id_suffix, nw_inf[index]['id']) + self.assertEquals('my_mac%s' % id_suffix, nw_inf[index]['address']) + self.assertEquals('10.0.%s.0/24' % id_suffix, + nw_inf[index]['network']['subnets'][0]['cidr']) + self.assertTrue(model.IP(address='8.8.%s.1' % id_suffix) in + nw_inf[index]['network']['subnets'][0]['dns']) + + def _get_instance_nw_info(self, number): + api = quantumapi.API() + self.mox.StubOutWithMock(api.db, 'instance_info_cache_update') + api.db.instance_info_cache_update(mox.IgnoreArg(), + self.instance['uuid'], + mox.IgnoreArg()) + port_data = number == 1 and self.port_data1 or self.port_data2 + self.moxed_client.list_ports( + tenant_id=self.instance['project_id'], + device_id=self.instance['uuid']).AndReturn( + {'ports': port_data}) + nets = number == 1 and self.nets1 or self.nets2 + self.moxed_client.list_networks( + tenant_id=self.instance['project_id']).AndReturn( + {'networks': nets}) + for i in xrange(1, number + 1): + subnet_data = i == 1 and self.subnet_data1 or self.subnet_data2 + self.moxed_client.list_subnets( + id=mox.SameElementsAs(['my_subid%s' % i])).AndReturn( + {'subnets': subnet_data}) + self.mox.ReplayAll() + nw_inf = api.get_instance_nw_info(self.context, self.instance) + for i in xrange(0, number): + self._verify_nw_info(nw_inf, i) + + def test_get_instance_nw_info_1(self): + """Test to get one port in one network and subnet.""" + self._get_instance_nw_info(1) + + def test_get_instance_nw_info_2(self): + """Test to get one port in each of two networks and subnets.""" + self._get_instance_nw_info(2) + + def test_get_instance_nw_info_with_nets(self): + """Test get instance_nw_info with networks passed in.""" + api = quantumapi.API() + self.mox.StubOutWithMock(api.db, 'instance_info_cache_update') + api.db.instance_info_cache_update( + mox.IgnoreArg(), + self.instance['uuid'], mox.IgnoreArg()) + self.moxed_client.list_ports( + tenant_id=self.instance['project_id'], + device_id=self.instance['uuid']).AndReturn( + {'ports': self.port_data1}) + self.moxed_client.list_subnets( + id=mox.SameElementsAs(['my_subid1'])).AndReturn( + {'subnets': self.subnet_data1}) + self.mox.ReplayAll() + nw_inf = api.get_instance_nw_info(self.context, + self.instance, + networks=self.nets1) + self._verify_nw_info(nw_inf, 0) + + def _allocate_for_instance(self, number): + api = quantumapi.API() + self.mox.StubOutWithMock(api, 'get_instance_nw_info') + nets = number == 1 and self.nets1 or self.nets2 + api.get_instance_nw_info(mox.IgnoreArg(), + self.instance, + networks=nets).AndReturn(None) + + self.moxed_client.list_networks( + tenant_id=self.instance['project_id']).AndReturn( + {'networks': nets}) + for network in nets: + port_req_body = { + 'port': { + 'network_id': network['id'], + 'admin_state_up': True, + 'device_id': self.instance['uuid'], + 'tenant_id': self.instance['project_id'], + }, + } + port = {'id': 'portid_' + network['id']} + self.moxed_client.create_port( + MyComparator(port_req_body)).AndReturn({'port': port}) + self.mox.ReplayAll() + api.allocate_for_instance(self.context, self.instance) + + def test_allocate_for_instance_1(self): + """Allocate one port in one network env.""" + self._allocate_for_instance(1) + + def test_allocate_for_instance_2(self): + """Allocate one port in two networks env.""" + self._allocate_for_instance(2) + + def test_allocate_for_instance_ex1(self): + """verify we will delete created ports + if we fail to allocate all net resources. + + Mox to raise exception when creating a second port. + In this case, the code should delete the first created port. + """ + api = quantumapi.API() + self.moxed_client.list_networks( + tenant_id=self.instance['project_id']).AndReturn( + {'networks': self.nets2}) + index = 0 + for network in self.nets2: + port_req_body = { + 'port': { + 'network_id': network['id'], + 'admin_state_up': True, + 'device_id': self.instance['uuid'], + 'tenant_id': self.instance['project_id'], + }, + } + port = {'id': 'portid_' + network['id']} + if index == 0: + self.moxed_client.create_port( + MyComparator(port_req_body)).AndReturn({'port': port}) + else: + self.moxed_client.create_port( + MyComparator(port_req_body)).AndRaise( + Exception("fail to create port")) + index += 1 + self.moxed_client.delete_port('portid_' + self.nets2[0]['id']) + self.mox.ReplayAll() + self.assertRaises(QUANTUM_CLIENT_EXCEPTION, api.allocate_for_instance, + self.context, self.instance) + + def test_allocate_for_instance_ex2(self): + """verify we have no port to delete + if we fail to allocate the first net resource. + + Mox to raise exception when creating the first port. + In this case, the code should not delete any ports. + """ + api = quantumapi.API() + self.moxed_client.list_networks( + tenant_id=self.instance['project_id']).AndReturn( + {'networks': self.nets2}) + port_req_body = { + 'port': { + 'network_id': self.nets2[0]['id'], + 'admin_state_up': True, + 'device_id': self.instance['uuid'], + 'tenant_id': self.instance['project_id'], + }, + } + self.moxed_client.create_port( + MyComparator(port_req_body)).AndRaise( + Exception("fail to create port")) + self.mox.ReplayAll() + self.assertRaises(QUANTUM_CLIENT_EXCEPTION, api.allocate_for_instance, + self.context, self.instance) + + def _deallocate_for_instance(self, number): + port_data = number == 1 and self.port_data1 or self.port_data2 + self.moxed_client.list_ports( + device_id=self.instance['uuid']).AndReturn( + {'ports': port_data}) + for port in port_data: + self.moxed_client.delete_port(port['id']) + self.mox.ReplayAll() + api = quantumapi.API() + api.deallocate_for_instance(self.context, self.instance) + + def test_deallocate_for_instance_1(self): + """Test to deallocate in one port env.""" + self._deallocate_for_instance(1) + + def test_deallocate_for_instance_2(self): + """Test to deallocate in two ports env.""" + self._deallocate_for_instance(2) + + def test_validate_networks(self): + requested_networks = [('my_netid1', 'test'), ('my_netid2', 'test2')] + self.moxed_client.list_networks( + id=mox.SameElementsAs(['my_netid1', 'my_netid2']), + tenant_id=self.context.project_id).AndReturn( + {'networks': self.nets2}) + self.mox.ReplayAll() + api = quantumapi.API() + api.validate_networks(self.context, requested_networks) + + def test_validate_networks_ex_1(self): + requested_networks = [('my_netid1', 'test'), ('my_netid2', 'test2')] + self.moxed_client.list_networks( + id=mox.SameElementsAs(['my_netid1', 'my_netid2']), + tenant_id=self.context.project_id).AndReturn( + {'networks': self.nets1}) + self.mox.ReplayAll() + api = quantumapi.API() + try: + api.validate_networks(self.context, requested_networks) + except exception.NetworkNotFound as ex: + self.assertTrue("my_netid2" in str(ex)) + + def test_validate_networks_ex_2(self): + requested_networks = [('my_netid1', 'test'), + ('my_netid2', 'test2'), + ('my_netid3', 'test3')] + self.moxed_client.list_networks( + id=mox.SameElementsAs(['my_netid1', 'my_netid2', 'my_netid3']), + tenant_id=self.context.project_id).AndReturn( + {'networks': self.nets1}) + self.mox.ReplayAll() + api = quantumapi.API() + try: + api.validate_networks(self.context, requested_networks) + except exception.NetworkNotFound as ex: + self.assertTrue("my_netid2, my_netid3" in str(ex)) + + def test_get_instance_uuids_by_ip_filter(self): + filters = {'ip': '^10\\.0\\.1\\.2$'} + self.moxed_client.list_ports( + fixed_ips=MyComparator({'ip_address': '10.0.1.2'})).AndReturn( + {'ports': self.port_data2}) + self.mox.ReplayAll() + api = quantumapi.API() + result = api.get_instance_uuids_by_ip_filter(self.context, filters) + self.assertEquals('device_id1', result[0]['instance_uuid']) + self.assertEquals('device_id2', result[1]['instance_uuid']) diff --git a/nova/tests/notifier/test_list_notifier.py b/nova/tests/notifier/test_list_notifier.py index bd1da9947..bb14fa80e 100644 --- a/nova/tests/notifier/test_list_notifier.py +++ b/nova/tests/notifier/test_list_notifier.py @@ -14,11 +14,11 @@ # under the License. import nova -from nova import log as logging import nova.notifier.api from nova.notifier import list_notifier import nova.notifier.log_notifier import nova.notifier.no_op_notifier +from nova.openstack.common import log as logging from nova import test diff --git a/nova/tests/test_SolidFireSanISCSIDriver.py b/nova/tests/test_SolidFireSanISCSIDriver.py index 9ebab6d5f..dd0e8d0de 100644 --- a/nova/tests/test_SolidFireSanISCSIDriver.py +++ b/nova/tests/test_SolidFireSanISCSIDriver.py @@ -16,7 +16,7 @@ # under the License. from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.volume import san diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py index afee021cd..1027a6a0a 100644 --- a/nova/tests/test_auth.py +++ b/nova/tests/test_auth.py @@ -22,7 +22,7 @@ from nova.auth import fakeldap from nova.auth import manager from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import test FLAGS = flags.FLAGS diff --git a/nova/tests/test_compute_utils.py b/nova/tests/test_compute_utils.py index 0955134ba..5081536ac 100644 --- a/nova/tests/test_compute_utils.py +++ b/nova/tests/test_compute_utils.py @@ -22,9 +22,9 @@ from nova.compute import utils as compute_utils from nova import context from nova import db from nova import flags -from nova import log as logging from nova.notifier import test_notifier from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import test from nova.tests import fake_network import nova.tests.image.fake @@ -114,6 +114,41 @@ class UsageInfoTestCase(test.TestCase): self.assertEquals(payload['image_ref_url'], image_ref_url) self.compute.terminate_instance(self.context, instance['uuid']) + def test_notify_usage_exists_deleted_instance(self): + """Ensure 'exists' notification generates appropriate usage data.""" + instance_id = self._create_instance() + instance = db.instance_get(self.context, instance_id) + # Set some system metadata + sys_metadata = {'image_md_key1': 'val1', + 'image_md_key2': 'val2', + 'other_data': 'meow'} + db.instance_system_metadata_update(self.context, instance['uuid'], + sys_metadata, False) + self.compute.terminate_instance(self.context, instance['uuid']) + instance = db.instance_get(self.context.elevated(read_deleted='yes'), + instance_id) + compute_utils.notify_usage_exists(self.context, instance) + msg = test_notifier.NOTIFICATIONS[-1] + self.assertEquals(msg['priority'], 'INFO') + self.assertEquals(msg['event_type'], 'compute.instance.exists') + payload = msg['payload'] + self.assertEquals(payload['tenant_id'], self.project_id) + self.assertEquals(payload['user_id'], self.user_id) + self.assertEquals(payload['instance_id'], instance.uuid) + self.assertEquals(payload['instance_type'], 'm1.tiny') + type_id = instance_types.get_instance_type_by_name('m1.tiny')['id'] + self.assertEquals(str(payload['instance_type_id']), str(type_id)) + for attr in ('display_name', 'created_at', 'launched_at', + 'state', 'state_description', + 'bandwidth', 'audit_period_beginning', + 'audit_period_ending', 'image_meta'): + self.assertTrue(attr in payload, + msg="Key %s not in payload" % attr) + self.assertEquals(payload['image_meta'], + {'md_key1': 'val1', 'md_key2': 'val2'}) + image_ref_url = "%s/images/1" % utils.generate_glance_url() + self.assertEquals(payload['image_ref_url'], image_ref_url) + def test_notify_usage_exists_instance_not_found(self): """Ensure 'exists' notification generates appropriate usage data.""" instance_id = self._create_instance() diff --git a/nova/tests/test_imagecache.py b/nova/tests/test_imagecache.py index 9ab10cc51..4ac7adfad 100644 --- a/nova/tests/test_imagecache.py +++ b/nova/tests/test_imagecache.py @@ -29,7 +29,7 @@ from nova import test from nova.compute import vm_states from nova import db from nova import flags -from nova import log +from nova.openstack.common import log from nova import utils from nova.virt.libvirt import imagecache from nova.virt.libvirt import utils as virtutils @@ -335,10 +335,10 @@ class ImageCacheManagerTestCase(test.TestCase): @contextlib.contextmanager def _intercept_log_messages(self): try: - mylog = log.getLogger() + mylog = log.getLogger('nova') stream = cStringIO.StringIO() handler = logging.StreamHandler(stream) - handler.setFormatter(log.LegacyNovaFormatter()) + handler.setFormatter(log.LegacyFormatter()) mylog.logger.addHandler(handler) yield stream finally: diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index 0d0417d0f..ed33a74b0 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -24,7 +24,7 @@ from nova.db.sqlalchemy import models from nova.db.sqlalchemy import session as sql_session from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import test FLAGS = flags.FLAGS diff --git a/nova/tests/test_ipv6.py b/nova/tests/test_ipv6.py index a09482ddd..b16855eb4 100644 --- a/nova/tests/test_ipv6.py +++ b/nova/tests/test_ipv6.py @@ -17,7 +17,7 @@ """Test suite for IPv6.""" from nova import ipv6 -from nova import log as logging +from nova.openstack.common import log as logging from nova import test LOG = logging.getLogger(__name__) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 55e8286ad..eed43b8d1 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -34,9 +34,9 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import test from nova.tests import fake_libvirt_utils from nova.tests import fake_network @@ -684,6 +684,79 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(conf.cpu.mode, "custom") self.assertEquals(conf.cpu.model, "Penryn") + @test.skip_if(missing_libvirt(), "Test requires libvirt") + def test_get_guest_cpu_config_host_passthrough_old(self): + def get_lib_version_stub(self): + return (0 * 1000 * 1000) + (9 * 1000) + 7 + + self.stubs.Set(libvirt.virConnect, "getLibVersion", + get_lib_version_stub) + conn = libvirt_driver.LibvirtDriver(True) + instance_ref = db.instance_create(self.context, self.test_instance) + + self.flags(libvirt_cpu_mode="host-passthrough") + self.assertRaises(exception.NovaException, + conn.get_guest_config, + instance_ref, + _fake_network_info(self.stubs, 1), + None, None) + + @test.skip_if(missing_libvirt(), "Test requires libvirt") + def test_get_guest_cpu_config_host_model_old(self): + def get_lib_version_stub(self): + return (0 * 1000 * 1000) + (9 * 1000) + 7 + + # Ensure we have a predictable host CPU + def get_host_capabilities_stub(self): + cpu = config.LibvirtConfigGuestCPU() + cpu.model = "Opteron_G4" + cpu.vendor = "AMD" + + caps = config.LibvirtConfigCaps() + caps.host = config.LibvirtConfigCapsHost() + caps.host.cpu = cpu + return caps + + self.stubs.Set(libvirt.virConnect, + "getLibVersion", + get_lib_version_stub) + self.stubs.Set(libvirt_driver.LibvirtDriver, + "get_host_capabilities", + get_host_capabilities_stub) + conn = libvirt_driver.LibvirtDriver(True) + instance_ref = db.instance_create(self.context, self.test_instance) + + self.flags(libvirt_cpu_mode="host-model") + conf = conn.get_guest_config(instance_ref, + _fake_network_info(self.stubs, 1), + None, None) + self.assertEquals(type(conf.cpu), + config.LibvirtConfigGuestCPU) + self.assertEquals(conf.cpu.mode, None) + self.assertEquals(conf.cpu.model, "Opteron_G4") + self.assertEquals(conf.cpu.vendor, "AMD") + + @test.skip_if(missing_libvirt(), "Test requires libvirt") + def test_get_guest_cpu_config_custom_old(self): + def get_lib_version_stub(self): + return (0 * 1000 * 1000) + (9 * 1000) + 7 + + self.stubs.Set(libvirt.virConnect, + "getLibVersion", + get_lib_version_stub) + conn = libvirt_driver.LibvirtDriver(True) + instance_ref = db.instance_create(self.context, self.test_instance) + + self.flags(libvirt_cpu_mode="custom") + self.flags(libvirt_cpu_model="Penryn") + conf = conn.get_guest_config(instance_ref, + _fake_network_info(self.stubs, 1), + None, None) + self.assertEquals(type(conf.cpu), + config.LibvirtConfigGuestCPU) + self.assertEquals(conf.cpu.mode, None) + self.assertEquals(conf.cpu.model, "Penryn") + def test_xml_and_uri_no_ramdisk_no_kernel(self): instance_data = dict(self.test_instance) self._check_xml_and_uri(instance_data, @@ -759,6 +832,7 @@ class LibvirtConnTestCase(test.TestCase): def test_list_instances(self): self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') libvirt_driver.LibvirtDriver._conn.lookupByID = self.fake_lookup + libvirt_driver.LibvirtDriver._conn.numOfDomains = lambda: 2 libvirt_driver.LibvirtDriver._conn.listDomainsID = lambda: [0, 1] self.mox.ReplayAll() @@ -810,6 +884,7 @@ class LibvirtConnTestCase(test.TestCase): return FakeVirtDomain(xml[id]) self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.numOfDomains = lambda: 4 libvirt_driver.LibvirtDriver._conn.listDomainsID = lambda: range(4) libvirt_driver.LibvirtDriver._conn.lookupByID = fake_lookup @@ -869,6 +944,7 @@ class LibvirtConnTestCase(test.TestCase): return FakeVirtDomain(xml[1]) self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, '_conn') + libvirt_driver.LibvirtDriver._conn.numOfDomains = lambda: 4 libvirt_driver.LibvirtDriver._conn.listDomainsID = lambda: range(4) libvirt_driver.LibvirtDriver._conn.lookupByID = fake_lookup libvirt_driver.LibvirtDriver._conn.lookupByName = fake_lookup_name diff --git a/nova/tests/test_log.py b/nova/tests/test_log.py deleted file mode 100644 index 9597b99c6..000000000 --- a/nova/tests/test_log.py +++ /dev/null @@ -1,217 +0,0 @@ -import cStringIO -import logging - -from nova import context -from nova import flags -from nova import log -from nova.notifier import api as notifier -from nova.openstack.common import jsonutils -from nova import test - -FLAGS = flags.FLAGS -flags.DECLARE('list_notifier_drivers', - 'nova.notifier.list_notifier') - - -def _fake_context(): - return context.RequestContext(1, 1) - - -class LoggerTestCase(test.TestCase): - def setUp(self): - super(LoggerTestCase, self).setUp() - self.log = log.getLogger() - - def test_handlers_have_nova_formatter(self): - formatters = [] - for h in self.log.logger.handlers: - f = h.formatter - if isinstance(f, log.LegacyNovaFormatter): - formatters.append(f) - self.assert_(formatters) - self.assertEqual(len(formatters), len(self.log.logger.handlers)) - - def test_handles_context_kwarg(self): - self.log.info("foo", context=_fake_context()) - self.assert_(True) # didn't raise exception - - def test_audit_handles_context_arg(self): - self.log.audit("foo", context=_fake_context()) - self.assert_(True) # didn't raise exception - - def test_will_be_verbose_if_verbose_flag_set(self): - self.flags(verbose=True) - log.setup() - self.assertEqual(logging.DEBUG, self.log.logger.getEffectiveLevel()) - - def test_will_not_be_verbose_if_verbose_flag_not_set(self): - self.flags(verbose=False) - log.setup() - self.assertEqual(logging.INFO, self.log.logger.getEffectiveLevel()) - - def test_no_logging_via_module(self): - for func in ('critical', 'error', 'exception', 'warning', 'warn', - 'info', 'debug', 'log', 'audit'): - self.assertRaises(AttributeError, getattr, log, func) - - -class LogHandlerTestCase(test.TestCase): - def test_log_path_logdir(self): - self.flags(logdir='/some/path', logfile=None) - self.assertEquals(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - def test_log_path_logfile(self): - self.flags(logfile='/some/path/foo-bar.log') - self.assertEquals(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - def test_log_path_none(self): - self.flags(logdir=None, logfile=None) - self.assertTrue(log._get_log_file_path(binary='foo-bar') is None) - - def test_log_path_logfile_overrides_logdir(self): - self.flags(logdir='/some/other/path', - logfile='/some/path/foo-bar.log') - self.assertEquals(log._get_log_file_path(binary='foo-bar'), - '/some/path/foo-bar.log') - - -class PublishErrorsHandlerTestCase(test.TestCase): - """Tests for nova.log.PublishErrorsHandler""" - def setUp(self): - super(PublishErrorsHandlerTestCase, self).setUp() - self.publiserrorshandler = log.PublishErrorsHandler(logging.ERROR) - - def test_emit_cfg_list_notifier_drivers_in_flags(self): - self.stub_flg = False - - def fake_notifier(*args, **kwargs): - self.stub_flg = True - - self.stubs.Set(notifier, 'notify', fake_notifier) - logrecord = logging.LogRecord('name', 'WARN', '/tmp', 1, - 'Message', None, None) - self.publiserrorshandler.emit(logrecord) - self.assertTrue(self.stub_flg) - - def test_emit_cfg_log_notifier_in_list_notifier_drivers(self): - self.flags(list_notifier_drivers=['nova.notifier.rabbit_notifier', - 'nova.notifier.log_notifier']) - self.stub_flg = True - - def fake_notifier(*args, **kwargs): - self.stub_flg = False - - self.stubs.Set(notifier, 'notify', fake_notifier) - logrecord = logging.LogRecord('name', 'WARN', '/tmp', 1, - 'Message', None, None) - self.publiserrorshandler.emit(logrecord) - self.assertTrue(self.stub_flg) - - -class NovaFormatterTestCase(test.TestCase): - 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.getLogger() - self.stream = cStringIO.StringIO() - self.handler = logging.StreamHandler(self.stream) - self.handler.setFormatter(log.LegacyNovaFormatter()) - self.log.logger.addHandler(self.handler) - self.level = self.log.logger.getEffectiveLevel() - self.log.logger.setLevel(logging.DEBUG) - - def tearDown(self): - self.log.logger.setLevel(self.level) - self.log.logger.removeHandler(self.handler) - super(NovaFormatterTestCase, self).tearDown() - - 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.TestCase): - def setUp(self): - super(NovaLoggerTestCase, self).setUp() - levels = FLAGS.default_log_levels - levels.append("nova-test=AUDIT") - self.flags(default_log_levels=levels, - verbose=True) - log.setup() - self.log = log.getLogger('nova-test') - - def test_has_level_from_flags(self): - self.assertEqual(logging.AUDIT, self.log.logger.getEffectiveLevel()) - - def test_child_log_has_level_of_parent_flag(self): - l = log.getLogger('nova-test.foo') - self.assertEqual(logging.AUDIT, l.logger.getEffectiveLevel()) - - -class JSONFormatterTestCase(test.TestCase): - def setUp(self): - super(JSONFormatterTestCase, self).setUp() - self.log = log.getLogger('test-json') - self.stream = cStringIO.StringIO() - handler = logging.StreamHandler(self.stream) - handler.setFormatter(log.JSONFormatter()) - self.log.logger.addHandler(handler) - self.log.logger.setLevel(logging.DEBUG) - - def test_json(self): - test_msg = 'This is a %(test)s line' - test_data = {'test': 'log'} - self.log.debug(test_msg, test_data) - - data = jsonutils.loads(self.stream.getvalue()) - self.assertTrue(data) - self.assertTrue('extra' in data) - self.assertEqual('test-json', data['name']) - - self.assertEqual(test_msg % test_data, data['message']) - self.assertEqual(test_msg, data['msg']) - self.assertEqual(test_data, data['args']) - - self.assertEqual('test_log.py', data['filename']) - self.assertEqual('test_json', data['funcname']) - - self.assertEqual('DEBUG', data['levelname']) - self.assertEqual(logging.DEBUG, data['levelno']) - self.assertFalse(data['traceback']) - - def test_json_exception(self): - test_msg = 'This is %s' - test_data = 'exceptional' - try: - raise Exception('This is exceptional') - except Exception: - self.log.exception(test_msg, test_data) - - data = jsonutils.loads(self.stream.getvalue()) - self.assertTrue(data) - self.assertTrue('extra' in data) - self.assertEqual('test-json', data['name']) - - self.assertEqual(test_msg % test_data, data['message']) - self.assertEqual(test_msg, data['msg']) - self.assertEqual([test_data], data['args']) - - self.assertEqual('ERROR', data['levelname']) - self.assertEqual(logging.ERROR, data['levelno']) - self.assertTrue(data['traceback']) diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py index fc654c80a..1e3ca5888 100644 --- a/nova/tests/test_migrations.py +++ b/nova/tests/test_migrations.py @@ -35,7 +35,7 @@ import sqlalchemy import nova.db.migration as migration import nova.db.sqlalchemy.migrate_repo from nova.db.sqlalchemy.migration import versioning_api as migration_api -from nova import log as logging +from nova.openstack.common import log as logging from nova import test diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py index 63c2773bf..ee77dca3b 100644 --- a/nova/tests/test_misc.py +++ b/nova/tests/test_misc.py @@ -42,33 +42,6 @@ class ExceptionTestCase(test.TestCase): class ProjectTestCase(test.TestCase): - def test_authors_up_to_date(self): - topdir = os.path.normpath(os.path.dirname(__file__) + '/../../') - missing = set() - contributors = set() - mailmap = utils.parse_mailmap(os.path.join(topdir, '.mailmap')) - authors_file = open(os.path.join(topdir, - 'Authors'), 'r').read().lower() - - if os.path.exists(os.path.join(topdir, '.git')): - for email in commands.getoutput('git log --format=%ae').split(): - if not email: - continue - if "jenkins" in email and "openstack.org" in email: - continue - email = '<' + email.lower() + '>' - contributors.add(utils.str_dict_replace(email, mailmap)) - else: - return - - for contributor in contributors: - if contributor == 'nova-core': - continue - if not contributor in authors_file: - missing.add(contributor) - - self.assertTrue(len(missing) == 0, - '%r not listed in Authors' % missing) def test_all_migrations_have_downgrade(self): topdir = os.path.normpath(os.path.dirname(__file__) + '/../../') diff --git a/nova/tests/test_netapp.py b/nova/tests/test_netapp.py index 8412420af..50b0f9c7e 100644 --- a/nova/tests/test_netapp.py +++ b/nova/tests/test_netapp.py @@ -25,7 +25,7 @@ import StringIO from lxml import etree -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.volume import netapp diff --git a/nova/tests/test_notifications.py b/nova/tests/test_notifications.py index 0f44064da..cf12d5629 100644 --- a/nova/tests/test_notifications.py +++ b/nova/tests/test_notifications.py @@ -25,10 +25,10 @@ from nova.compute import vm_states from nova import context from nova import db from nova import flags -from nova import log as logging import nova.network from nova import notifications from nova.notifier import test_notifier +from nova.openstack.common import log as logging from nova import test from nova.tests import fake_network diff --git a/nova/tests/test_notifier.py b/nova/tests/test_notifier.py index 773ffe49b..f7f54f374 100644 --- a/nova/tests/test_notifier.py +++ b/nova/tests/test_notifier.py @@ -16,9 +16,9 @@ import nova from nova import context from nova import flags -from nova import log from nova.notifier import api as notifier_api import nova.notifier.no_op_notifier +from nova.openstack.common import log from nova import test @@ -106,7 +106,7 @@ class NotifierTestCase(test.TestCase): 'nova.notifier.rabbit_notifier') self.stubs.Set(nova.flags.FLAGS, 'publish_errors', True) LOG = log.getLogger('nova') - log.setup() + log.setup('nova') msgs = [] def mock_notify(context, topic, data): diff --git a/nova/tests/test_nova_manage.py b/nova/tests/test_nova_manage.py index c9e2f1bbf..e515bd51d 100644 --- a/nova/tests/test_nova_manage.py +++ b/nova/tests/test_nova_manage.py @@ -234,62 +234,3 @@ class NetworkCommandsTestCase(test.TestCase): self._test_modify_base(update_value={'project_id': None, 'host': None}, project=None, host=None, dis_project=True, dis_host=True) - - -class ExportAuthTestCase(test.TestCase): - - def test_export_with_noauth(self): - self._do_test_export() - - def test_export_with_deprecated_auth(self): - self.flags(auth_strategy='deprecated') - self._do_test_export(noauth=False) - - def _do_test_export(self, noauth=True): - self.flags(allowed_roles=['role1', 'role2']) - am = nova.auth.manager.AuthManager(new=True) - user1 = am.create_user('user1', 'a1', 's1') - user2 = am.create_user('user2', 'a2', 's2') - user3 = am.create_user('user3', 'a3', 's3') - proj1 = am.create_project('proj1', user1, member_users=[user1, user2]) - proj2 = am.create_project('proj2', user2, member_users=[user2, user3]) - am.add_role(user1, 'role1', proj1) - am.add_role(user1, 'role1', proj2) - am.add_role(user3, 'role1', proj1) - am.add_role(user3, 'role2', proj2) - - commands = nova_manage.ExportCommands() - output = commands._get_auth_data() - - def pw(idx): - return ('user' if noauth else 'a') + str(idx) - - expected = { - "users": [ - {"id": "user1", "name": "user1", 'password': pw(1)}, - {"id": "user2", "name": "user2", 'password': pw(2)}, - {"id": "user3", "name": "user3", 'password': pw(3)}, - ], - "roles": ["role1", "role2"], - "role_user_tenant_list": [ - {"user_id": "user1", "role": "role1", "tenant_id": "proj1"}, - {"user_id": "user3", "role": "role2", "tenant_id": "proj2"}, - ], - "user_tenant_list": [ - {"tenant_id": "proj1", "user_id": "user1"}, - {"tenant_id": "proj1", "user_id": "user2"}, - {"tenant_id": "proj2", "user_id": "user2"}, - {"tenant_id": "proj2", "user_id": "user3"}, - ], - "ec2_credentials": [ - {"access_key": pw(1), "secret_key": "s1", "user_id": "user1"}, - {"access_key": pw(2), "secret_key": "s2", "user_id": "user2"}, - {"access_key": pw(3), "secret_key": "s3", "user_id": "user3"}, - ], - "tenants": [ - {"description": "proj1", "id": "proj1", "name": "proj1"}, - {"description": "proj2", "id": "proj2", "name": "proj2"}, - ], - } - - self.assertDictMatch(output, expected) diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py index 388f075af..c4aa828ad 100644 --- a/nova/tests/test_virt.py +++ b/nova/tests/test_virt.py @@ -15,8 +15,10 @@ # License for the specific language governing permissions and limitations # under the License. +from nova import exception from nova import flags from nova import test +from nova.virt.disk import api as disk_api from nova.virt import driver FLAGS = flags.FLAGS @@ -81,3 +83,21 @@ class TestVirtDriver(test.TestCase): 'swap_size': 0})) self.assertTrue(driver.swap_is_usable({'device_name': '/dev/sdb', 'swap_size': 1})) + + +class TestVirtDisk(test.TestCase): + def test_check_safe_path(self): + ret = disk_api._join_and_check_path_within_fs('/foo', 'etc', + 'something.conf') + self.assertEquals(ret, '/foo/etc/something.conf') + + def test_check_unsafe_path(self): + self.assertRaises(exception.Invalid, + disk_api._join_and_check_path_within_fs, + '/foo', 'etc/../../../something.conf') + + def test_inject_files_with_bad_path(self): + self.assertRaises(exception.Invalid, + disk_api._inject_file_into_fs, + '/tmp', '/etc/../../../../etc/passwd', + 'hax') diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index c15061f48..e95d15ae5 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -21,8 +21,8 @@ import traceback from nova.compute.manager import ComputeManager from nova import exception -from nova import log as logging from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import test from nova.tests.image import fake as fake_image from nova.tests import utils as test_utils @@ -79,7 +79,8 @@ class _FakeDriverBackendTestCase(test.TestCase): self.flags(firewall_driver=nova.virt.libvirt.firewall.drivers[0], rescue_image_id="2", rescue_kernel_id="3", - rescue_ramdisk_id=None) + rescue_ramdisk_id=None, + libvirt_snapshots_directory='./') def fake_extend(image, size): pass @@ -117,9 +118,13 @@ class VirtDriverLoaderTestCase(_FakeDriverBackendTestCase): final class""" # if your driver supports being tested in a fake way, it can go here + # + # both long form and short form drivers are supported new_drivers = { 'nova.virt.fake.FakeDriver': 'FakeDriver', - 'nova.virt.libvirt.LibvirtDriver': 'LibvirtDriver' + 'nova.virt.libvirt.LibvirtDriver': 'LibvirtDriver', + 'fake.FakeDriver': 'FakeDriver', + 'libvirt.LibvirtDriver': 'LibvirtDriver' } # NOTE(sdague): remove after Folsom release when connection_type diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py index 0aac9e8cf..2e149f950 100644 --- a/nova/tests/test_volume.py +++ b/nova/tests/test_volume.py @@ -28,9 +28,9 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.notifier import test_notifier from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc import nova.policy from nova import quota @@ -430,7 +430,7 @@ class DriverTestCase(test.TestCase): return self.output, None self.volume.driver.set_execute(_fake_execute) - log = logging.getLogger() + log = logging.getLogger('nova') self.stream = cStringIO.StringIO() log.logger.addHandler(logging.logging.StreamHandler(self.stream)) diff --git a/nova/tests/test_volume_types.py b/nova/tests/test_volume_types.py index d2680afaf..764cf0343 100644 --- a/nova/tests/test_volume_types.py +++ b/nova/tests/test_volume_types.py @@ -23,7 +23,7 @@ from nova.db.sqlalchemy import models from nova.db.sqlalchemy import session as sql_session from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.volume import volume_types diff --git a/nova/tests/test_volume_utils.py b/nova/tests/test_volume_utils.py index b2ccb41b8..1db0f9eef 100644 --- a/nova/tests/test_volume_utils.py +++ b/nova/tests/test_volume_utils.py @@ -20,9 +20,9 @@ from nova import context from nova import db from nova import flags -from nova import log as logging from nova.notifier import test_notifier from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import test from nova.volume import utils as volume_utils diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index dd480f347..3138ef0b9 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -18,6 +18,7 @@ import ast import contextlib +import cPickle as pickle import functools import os import re @@ -29,8 +30,9 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging +from nova.image import glance from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import test from nova.tests.db import fakes as db_fakes @@ -134,20 +136,18 @@ def stub_vm_utils_with_vdi_attached_here(function, should_return=True): return decorated_function -class XenAPIVolumeTestCase(test.TestCase): +class XenAPIVolumeTestCase(stubs.XenAPITestBase): """Unit tests for Volume operations.""" def setUp(self): super(XenAPIVolumeTestCase, self).setUp() self.user_id = 'fake' self.project_id = 'fake' self.context = context.RequestContext(self.user_id, self.project_id) - self.flags(target_host='127.0.0.1', - xenapi_connection_url='test_url', - xenapi_connection_password='test_pass', - firewall_driver='nova.virt.xenapi.firewall.' - 'Dom0IptablesFirewallDriver') + self.flags(xenapi_connection_url='test_url', + xenapi_connection_password='test_pass', + firewall_driver='nova.virt.xenapi.firewall.' + 'Dom0IptablesFirewallDriver') db_fakes.stub_out_db_instance_api(self.stubs) - xenapi_fake.reset() self.instance_values = {'id': 1, 'project_id': self.user_id, 'user_id': 'fake', @@ -252,7 +252,7 @@ class XenAPIVolumeTestCase(test.TestCase): '/dev/sdc') -class XenAPIVMTestCase(test.TestCase): +class XenAPIVMTestCase(stubs.XenAPITestBase): """Unit tests for VM operations.""" def setUp(self): super(XenAPIVMTestCase, self).setUp() @@ -262,7 +262,6 @@ class XenAPIVMTestCase(test.TestCase): instance_name_template='%d', firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver') - xenapi_fake.reset() xenapi_fake.create_local_srs() xenapi_fake.create_local_pifs() db_fakes.stub_out_db_instance_api(self.stubs) @@ -756,8 +755,11 @@ class XenAPIVMTestCase(test.TestCase): session = xenapi_conn.XenAPISession('test_url', 'root', 'test_pass') vm_ref = vm_utils.lookup(session, instance.name) - xenapi_fake.create_vbd(vm_ref, "swap", userdevice=1) - xenapi_fake.create_vbd(vm_ref, "rootfs", userdevice=0) + swap_vdi_ref = xenapi_fake.create_vdi('swap', None) + root_vdi_ref = xenapi_fake.create_vdi('root', None) + + xenapi_fake.create_vbd(vm_ref, swap_vdi_ref, userdevice=1) + xenapi_fake.create_vbd(vm_ref, root_vdi_ref, userdevice=0) conn = xenapi_conn.XenAPIDriver(False) image_meta = {'id': IMAGE_VHD, @@ -868,19 +870,17 @@ class XenAPIDiffieHellmanTestCase(test.TestCase): self._test_encryption(''.join(['abcd' for i in xrange(1024)])) -class XenAPIMigrateInstance(test.TestCase): +class XenAPIMigrateInstance(stubs.XenAPITestBase): """Unit test for verifying migration-related actions.""" def setUp(self): super(XenAPIMigrateInstance, self).setUp() - self.flags(target_host='127.0.0.1', - xenapi_connection_url='test_url', - xenapi_connection_password='test_pass', - firewall_driver='nova.virt.xenapi.firewall.' - 'Dom0IptablesFirewallDriver') + self.flags(xenapi_connection_url='test_url', + xenapi_connection_password='test_pass', + firewall_driver='nova.virt.xenapi.firewall.' + 'Dom0IptablesFirewallDriver') stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) db_fakes.stub_out_db_instance_api(self.stubs) - xenapi_fake.reset() xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) self.user_id = 'fake' self.project_id = 'fake' @@ -1072,17 +1072,6 @@ class XenAPIImageTypeTestCase(test.TestCase): class XenAPIDetermineDiskImageTestCase(test.TestCase): """Unit tests for code that detects the ImageType.""" - def setUp(self): - super(XenAPIDetermineDiskImageTestCase, self).setUp() - - class FakeInstance(object): - pass - - self.fake_instance = FakeInstance() - self.fake_instance.id = 42 - self.fake_instance.os_type = 'linux' - self.fake_instance.architecture = 'x86-64' - def assert_disk_type(self, image_meta, expected_disk_type): actual = vm_utils.determine_disk_image_type(image_meta) self.assertEqual(expected_disk_type, actual) @@ -1122,7 +1111,7 @@ class CompareVersionTestCase(test.TestCase): self.assertTrue(vmops.cmp_version('1.2.3', '1.2.3.4') < 0) -class XenAPIHostTestCase(test.TestCase): +class XenAPIHostTestCase(stubs.XenAPITestBase): """Tests HostState, which holds metrics from XenServer that get reported back to the Schedulers.""" @@ -1131,7 +1120,6 @@ class XenAPIHostTestCase(test.TestCase): self.flags(xenapi_connection_url='test_url', xenapi_connection_password='test_pass') stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) - xenapi_fake.reset() xenapi_fake.create_local_srs() self.conn = xenapi_conn.XenAPIDriver(False) @@ -1179,16 +1167,14 @@ class XenAPIHostTestCase(test.TestCase): self.assertEqual(result, 'fake uptime') -class XenAPIAutoDiskConfigTestCase(test.TestCase): +class XenAPIAutoDiskConfigTestCase(stubs.XenAPITestBase): def setUp(self): super(XenAPIAutoDiskConfigTestCase, self).setUp() - self.flags(target_host='127.0.0.1', - xenapi_connection_url='test_url', + self.flags(xenapi_connection_url='test_url', xenapi_connection_password='test_pass', firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver') stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) - xenapi_fake.reset() self.conn = xenapi_conn.XenAPIDriver(False) self.user_id = 'fake' @@ -1270,19 +1256,17 @@ class XenAPIAutoDiskConfigTestCase(test.TestCase): self.assertIsPartitionCalled(True) -class XenAPIGenerateLocal(test.TestCase): +class XenAPIGenerateLocal(stubs.XenAPITestBase): """Test generating of local disks, like swap and ephemeral""" def setUp(self): super(XenAPIGenerateLocal, self).setUp() - self.flags(target_host='127.0.0.1', - xenapi_connection_url='test_url', + self.flags(xenapi_connection_url='test_url', xenapi_connection_password='test_pass', xenapi_generate_swap=True, firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver') stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) db_fakes.stub_out_db_instance_api(self.stubs) - xenapi_fake.reset() self.conn = xenapi_conn.XenAPIDriver(False) self.user_id = 'fake' @@ -1347,18 +1331,16 @@ class XenAPIGenerateLocal(test.TestCase): self.assertCalled(instance) -class XenAPIBWUsageTestCase(test.TestCase): +class XenAPIBWUsageTestCase(stubs.XenAPITestBase): def setUp(self): super(XenAPIBWUsageTestCase, self).setUp() self.stubs.Set(vm_utils, 'compile_metrics', XenAPIBWUsageTestCase._fake_compile_metrics) - self.flags(target_host='127.0.0.1', - xenapi_connection_url='test_url', + self.flags(xenapi_connection_url='test_url', xenapi_connection_password='test_pass', firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver') stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) - xenapi_fake.reset() self.conn = xenapi_conn.XenAPIDriver(False) @classmethod @@ -1382,7 +1364,7 @@ class XenAPIBWUsageTestCase(test.TestCase): # TODO(salvatore-orlando): this class and # nova.tests.test_libvirt.IPTablesFirewallDriverTestCase share a lot of code. # Consider abstracting common code in a base class for firewall driver testing. -class XenAPIDom0IptablesFirewallTestCase(test.TestCase): +class XenAPIDom0IptablesFirewallTestCase(stubs.XenAPITestBase): _in_nat_rules = [ '# Generated by iptables-save v1.4.10 on Sat Feb 19 00:03:19 2011', @@ -1428,7 +1410,6 @@ class XenAPIDom0IptablesFirewallTestCase(test.TestCase): instance_name_template='%d', firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver') - xenapi_fake.reset() xenapi_fake.create_local_srs() xenapi_fake.create_local_pifs() self.user_id = 'mappin' @@ -1683,12 +1664,8 @@ class XenAPIDom0IptablesFirewallTestCase(test.TestCase): self.assertEqual(1, len(rules)) -class XenAPISRSelectionTestCase(test.TestCase): +class XenAPISRSelectionTestCase(stubs.XenAPITestBase): """Unit tests for testing we find the right SR.""" - def setUp(self): - super(XenAPISRSelectionTestCase, self).setUp() - xenapi_fake.reset() - def test_safe_find_sr_raise_exception(self): """Ensure StorageRepositoryNotFound is raise when wrong filter.""" self.flags(sr_matching_filter='yadayadayada') @@ -1737,7 +1714,7 @@ class XenAPISRSelectionTestCase(test.TestCase): expected) -class XenAPIAggregateTestCase(test.TestCase): +class XenAPIAggregateTestCase(stubs.XenAPITestBase): """Unit tests for aggregate operations.""" def setUp(self): super(XenAPIAggregateTestCase, self).setUp() @@ -1748,7 +1725,6 @@ class XenAPIAggregateTestCase(test.TestCase): firewall_driver='nova.virt.xenapi.firewall.' 'Dom0IptablesFirewallDriver', host='host') - xenapi_fake.reset() host_ref = xenapi_fake.get_all('host')[0] stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) self.context = context.get_admin_context() @@ -1879,3 +1855,52 @@ class XenAPIAggregateTestCase(test.TestCase): if metadata: db.aggregate_metadata_add(self.context, result.id, metadata) return db.aggregate_get(self.context, result.id) + + +class VmUtilsTestCase(test.TestCase): + """Unit tests for xenapi utils.""" + + def test_upload_image(self): + """Ensure image properties include instance system metadata + as well as few local settings.""" + def fake_pick_glance_api_server(): + return ("host", 80) + + def fake_instance_system_metadata_get(context, uuid): + return dict(image_a=1, image_b=2, image_c='c', d='d') + + def fake_get_sr_path(session): + return "foo" + + class FakeInstance(object): + auto_disk_config = "auto disk config" + os_type = "os type" + + def __getitem__(instance_self, item): + return "whatever" + + class FakeSession(object): + def call_plugin(session_self, service, command, kwargs): + self.kwargs = kwargs + + def fake_dumps(thing): + return thing + + self.stubs.Set(glance, "pick_glance_api_server", + fake_pick_glance_api_server) + self.stubs.Set(db, "instance_system_metadata_get", + fake_instance_system_metadata_get) + self.stubs.Set(vm_utils, "get_sr_path", fake_get_sr_path) + self.stubs.Set(pickle, "dumps", fake_dumps) + + ctx = context.get_admin_context() + + instance = FakeInstance() + session = FakeSession() + vm_utils.upload_image(ctx, session, instance, "vmi uuids", "image id") + + actual = self.kwargs['params']['properties'] + expected = dict(a=1, b=2, c='c', d='d', + auto_disk_config='auto disk config', + os_type='os type') + self.assertEquals(expected, actual) diff --git a/nova/tests/test_xensm.py b/nova/tests/test_xensm.py index 638a359fa..659a7b92f 100644 --- a/nova/tests/test_xensm.py +++ b/nova/tests/test_xensm.py @@ -22,7 +22,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.openstack.common import log as logging from nova import test from nova.tests.xenapi import stubs from nova.virt.xenapi import connection as xenapi_conn @@ -35,7 +35,7 @@ LOG = logging.getLogger(__name__) FLAGS = flags.FLAGS -class XenSMTestCase(test.TestCase): +class XenSMTestCase(stubs.XenAPITestBase): """Unit tests for Xen Storage Manager Volume operations.""" def _get_sm_backend_params(self): diff --git a/nova/tests/volume/test_HpSanISCSIDriver.py b/nova/tests/volume/test_HpSanISCSIDriver.py index 705152536..b3656bb22 100644 --- a/nova/tests/volume/test_HpSanISCSIDriver.py +++ b/nova/tests/volume/test_HpSanISCSIDriver.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import test from nova.volume import san diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 255f26a22..275e3ce63 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -16,8 +16,10 @@ """Stubouts, mocks and fixtures for the test suite""" import random +import sys from nova.openstack.common import jsonutils +from nova import test import nova.tests.image.fake from nova.virt.xenapi import connection as xenapi_conn from nova.virt.xenapi import fake @@ -51,17 +53,9 @@ def stubout_instance_snapshot(stubs): def stubout_session(stubs, cls, product_version=(5, 6, 2), **opt_args): - """Stubs out three methods from XenAPISession""" - def fake_import(self): - """Stubs out get_imported_xenapi of XenAPISession""" - fake_module = 'nova.virt.xenapi.fake' - from_list = ['fake'] - return __import__(fake_module, globals(), locals(), from_list, -1) - + """Stubs out methods from XenAPISession""" stubs.Set(xenapi_conn.XenAPISession, '_create_session', lambda s, url: cls(url, **opt_args)) - stubs.Set(xenapi_conn.XenAPISession, 'get_imported_xenapi', - fake_import) stubs.Set(xenapi_conn.XenAPISession, '_get_product_version', lambda s: product_version) @@ -363,3 +357,22 @@ def stub_out_migration_methods(stubs): stubs.Set(vm_utils, 'get_vdi_for_vm_safely', fake_get_vdi) stubs.Set(vm_utils, 'get_sr_path', fake_get_sr_path) stubs.Set(vm_utils, 'generate_ephemeral', fake_generate_ephemeral) + + +class XenAPITestBase(test.TestCase): + def setUp(self): + super(XenAPITestBase, self).setUp() + + self.orig_XenAPI = sys.modules.get('XenAPI') + sys.modules['XenAPI'] = fake + + fake.reset() + + def tearDown(self): + if self.orig_XenAPI is not None: + sys.modules['XenAPI'] = self.orig_XenAPI + self.orig_XenAPI = None + else: + sys.modules.pop('XenAPI') + + super(XenAPITestBase, self).tearDown() diff --git a/nova/utils.py b/nova/utils.py index 9560c1d18..b9af41fca 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -51,10 +51,10 @@ import netaddr from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils diff --git a/nova/virt/baremetal/dom.py b/nova/virt/baremetal/dom.py index 0890de5d4..350506f73 100644 --- a/nova/virt/baremetal/dom.py +++ b/nova/virt/baremetal/dom.py @@ -17,8 +17,8 @@ from nova.compute import power_state from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.virt.baremetal import nodes FLAGS = flags.FLAGS diff --git a/nova/virt/baremetal/proxy.py b/nova/virt/baremetal/proxy.py index f1aa908bb..56ed12715 100644 --- a/nova/virt/baremetal/proxy.py +++ b/nova/virt/baremetal/proxy.py @@ -40,9 +40,9 @@ from nova import context as nova_context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova import notifications from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.virt.baremetal import dom from nova.virt.baremetal import nodes diff --git a/nova/virt/baremetal/tilera.py b/nova/virt/baremetal/tilera.py index 36d499cc2..4d4a37007 100644 --- a/nova/virt/baremetal/tilera.py +++ b/nova/virt/baremetal/tilera.py @@ -28,8 +28,8 @@ import time from nova.compute import power_state from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils FLAGS = flags.FLAGS diff --git a/nova/virt/connection.py b/nova/virt/connection.py index 079675da3..d7a0f56de 100644 --- a/nova/virt/connection.py +++ b/nova/virt/connection.py @@ -24,8 +24,8 @@ import sys from nova.common import deprecated from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova import utils from nova.virt import driver @@ -33,11 +33,11 @@ LOG = logging.getLogger(__name__) FLAGS = flags.FLAGS known_drivers = { - 'baremetal': 'nova.virt.baremetal.proxy.ProxyConnection', - 'fake': 'nova.virt.fake.FakeDriver', - 'libvirt': 'nova.virt.libvirt.LibvirtDriver', - 'vmwareapi': 'nova.virt.vmwareapi_conn.VMWareESXDriver', - 'xenapi': 'nova.virt.xenapi.connection.XenAPIDriver' + 'baremetal': 'baremetal.proxy.ProxyConnection', + 'fake': 'fake.FakeDriver', + 'libvirt': 'libvirt.LibvirtDriver', + 'vmwareapi': 'vmwareapi_conn.VMWareESXDriver', + 'xenapi': 'xenapi.connection.XenAPIDriver' } @@ -75,7 +75,8 @@ def get_connection(read_only=False): if driver_name is None: raise exception.VirtDriverNotFound(name=FLAGS.connection_type) - conn = importutils.import_object(driver_name, read_only=read_only) + conn = importutils.import_object_ns('nova.virt', driver_name, + read_only=read_only) if conn is None: LOG.error(_('Failed to open connection to underlying virt platform')) diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py index 672b5d50e..766c9ebd7 100644 --- a/nova/virt/disk/api.py +++ b/nova/virt/disk/api.py @@ -33,9 +33,9 @@ import tempfile from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import utils from nova.virt.disk import guestfs from nova.virt.disk import loop @@ -271,14 +271,15 @@ def setup_container(image, container_dir=None, use_cow=False): LXC does not support qcow2 images yet. """ - try: - img = _DiskImage(image=image, use_cow=use_cow, mount_dir=container_dir) - if img.mount(): - return img - else: - raise exception.NovaException(img.errors) - except Exception, exn: - LOG.exception(_('Failed to mount filesystem: %s'), exn) + img = _DiskImage(image=image, use_cow=use_cow, mount_dir=container_dir) + if img.mount(): + return img + else: + LOG.error(_("Failed to mount container filesystem '%(image)s' " + "on '%(target)s': %(errors)s") % + {"image": img, "target": container_dir, + "errors": img.errors}) + raise exception.NovaException(img.errors) def destroy_container(img): @@ -293,7 +294,7 @@ def destroy_container(img): if img: img.umount() except Exception, exn: - LOG.exception(_('Failed to remove container: %s'), exn) + LOG.exception(_('Failed to unmount container filesystem: %s'), exn) def inject_data_into_fs(fs, key, net, metadata, admin_password, execute): @@ -311,20 +312,39 @@ def inject_data_into_fs(fs, key, net, metadata, admin_password, execute): _inject_admin_password_into_fs(admin_password, fs, execute=execute) -def _inject_file_into_fs(fs, path, contents): - absolute_path = os.path.join(fs, path.lstrip('/')) +def _join_and_check_path_within_fs(fs, *args): + '''os.path.join() with safety check for injected file paths. + + Join the supplied path components and make sure that the + resulting path we are injecting into is within the + mounted guest fs. Trying to be clever and specifying a + path with '..' in it will hit this safeguard. + ''' + absolute_path = os.path.realpath(os.path.join(fs, *args)) + if not absolute_path.startswith(os.path.realpath(fs) + '/'): + raise exception.Invalid(_('injected file path not valid')) + return absolute_path + + +def _inject_file_into_fs(fs, path, contents, append=False): + absolute_path = _join_and_check_path_within_fs(fs, path.lstrip('/')) + parent_dir = os.path.dirname(absolute_path) utils.execute('mkdir', '-p', parent_dir, run_as_root=True) - utils.execute('tee', absolute_path, process_input=contents, - run_as_root=True) + + args = [] + if append: + args.append('-a') + args.append(absolute_path) + + kwargs = dict(process_input=contents, run_as_root=True) + + utils.execute('tee', *args, **kwargs) def _inject_metadata_into_fs(metadata, fs, execute=None): - metadata_path = os.path.join(fs, "meta.js") metadata = dict([(m.key, m.value) for m in metadata]) - - utils.execute('tee', metadata_path, - process_input=jsonutils.dumps(metadata), run_as_root=True) + _inject_file_into_fs(fs, 'meta.js', jsonutils.dumps(metadata)) def _inject_key_into_fs(key, fs, execute=None): @@ -333,20 +353,22 @@ def _inject_key_into_fs(key, fs, execute=None): key is an ssh key string. fs is the path to the base of the filesystem into which to inject the key. """ - sshdir = os.path.join(fs, 'root', '.ssh') + sshdir = _join_and_check_path_within_fs(fs, 'root', '.ssh') utils.execute('mkdir', '-p', sshdir, run_as_root=True) utils.execute('chown', 'root', sshdir, run_as_root=True) utils.execute('chmod', '700', sshdir, run_as_root=True) - keyfile = os.path.join(sshdir, 'authorized_keys') - key_data = [ + + keyfile = os.path.join('root', '.ssh', 'authorized_keys') + + key_data = ''.join([ '\n', '# The following ssh key was injected by Nova', '\n', key.strip(), '\n', - ] - utils.execute('tee', '-a', keyfile, - process_input=''.join(key_data), run_as_root=True) + ]) + + _inject_file_into_fs(fs, keyfile, key_data, append=True) def _inject_net_into_fs(net, fs, execute=None): @@ -354,12 +376,13 @@ def _inject_net_into_fs(net, fs, execute=None): net is the contents of /etc/network/interfaces. """ - netdir = os.path.join(os.path.join(fs, 'etc'), 'network') + netdir = _join_and_check_path_within_fs(fs, 'etc', 'network') utils.execute('mkdir', '-p', netdir, run_as_root=True) utils.execute('chown', 'root:root', netdir, run_as_root=True) utils.execute('chmod', 755, netdir, run_as_root=True) - netfile = os.path.join(netdir, 'interfaces') - utils.execute('tee', netfile, process_input=net, run_as_root=True) + + netfile = os.path.join('etc', 'network', 'interfaces') + _inject_file_into_fs(fs, netfile, net) def _inject_admin_password_into_fs(admin_passwd, fs, execute=None): @@ -384,16 +407,15 @@ def _inject_admin_password_into_fs(admin_passwd, fs, execute=None): fd, tmp_shadow = tempfile.mkstemp() os.close(fd) - utils.execute('cp', os.path.join(fs, 'etc', 'passwd'), tmp_passwd, - run_as_root=True) - utils.execute('cp', os.path.join(fs, 'etc', 'shadow'), tmp_shadow, - run_as_root=True) + passwd_path = _join_and_check_path_within_fs(fs, 'etc', 'passwd') + shadow_path = _join_and_check_path_within_fs(fs, 'etc', 'shadow') + + utils.execute('cp', passwd_path, tmp_passwd, run_as_root=True) + utils.execute('cp', shadow_path, tmp_shadow, run_as_root=True) _set_passwd(admin_user, admin_passwd, tmp_passwd, tmp_shadow) - utils.execute('cp', tmp_passwd, os.path.join(fs, 'etc', 'passwd'), - run_as_root=True) + utils.execute('cp', tmp_passwd, passwd_path, run_as_root=True) os.unlink(tmp_passwd) - utils.execute('cp', tmp_shadow, os.path.join(fs, 'etc', 'shadow'), - run_as_root=True) + utils.execute('cp', tmp_shadow, shadow_path, run_as_root=True) os.unlink(tmp_shadow) diff --git a/nova/virt/disk/mount.py b/nova/virt/disk/mount.py index 11959b2f6..781167753 100644 --- a/nova/virt/disk/mount.py +++ b/nova/virt/disk/mount.py @@ -17,7 +17,7 @@ import os -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils LOG = logging.getLogger(__name__) diff --git a/nova/virt/driver.py b/nova/virt/driver.py index 52e720801..ad73b1896 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -24,7 +24,7 @@ Driver base-classes: from nova.compute import power_state from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index f13f71a8d..85253c0fa 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -28,7 +28,7 @@ semantics of real hypervisor connections. from nova.compute import power_state from nova import db from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils from nova.virt import driver diff --git a/nova/virt/firewall.py b/nova/virt/firewall.py index 89559a829..2afb8b6cf 100644 --- a/nova/virt/firewall.py +++ b/nova/virt/firewall.py @@ -20,8 +20,8 @@ from nova import context from nova import db from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.virt import netutils diff --git a/nova/virt/images.py b/nova/virt/images.py index 78bd8aebc..c80e83e23 100644 --- a/nova/virt/images.py +++ b/nova/virt/images.py @@ -26,8 +26,8 @@ import os from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index edde66d7c..da55dc234 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -22,7 +22,7 @@ and support conversion to/from XML """ from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from lxml import etree diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 96ae61d70..14f5620c9 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -61,11 +61,11 @@ from nova import db from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import utils from nova.virt.disk import api as disk from nova.virt import driver @@ -176,6 +176,10 @@ libvirt_opts = [ help='Set to a named libvirt CPU model (see names listed ' 'in /usr/share/libvirt/cpu_map.xml). Only has effect if ' 'libvirt_cpu_mode="custom" and libvirt_type="kvm|qemu"'), + cfg.StrOpt('libvirt_snapshots_directory', + default='$instances_path/snapshots', + help='Location where libvirt driver will store snapshots ' + 'before uploading them to image service'), ] FLAGS = flags.FLAGS @@ -231,6 +235,9 @@ LIBVIRT_POWER_STATE = { } MIN_LIBVIRT_VERSION = (0, 9, 6) +# When the above version matches/exceeds this version +# delete it & corresponding code using it +MIN_LIBVIRT_HOST_CPU_VERSION = (0, 9, 10) def _late_load_cheetah(): @@ -390,9 +397,15 @@ class LibvirtDriver(driver.ComputeDriver): except libvirt.libvirtError: return False + # TODO(Shrews): Remove when libvirt Bugzilla bug # 836647 is fixed. + def list_instance_ids(self): + if self._conn.numOfDomains() == 0: + return [] + return self._conn.listDomainsID() + def list_instances(self): return [self._conn.lookupByID(x).name() - for x in self._conn.listDomainsID() + for x in self.list_instance_ids() if x != 0] # We skip domains with ID 0 (hypervisors). @staticmethod @@ -415,7 +428,7 @@ class LibvirtDriver(driver.ComputeDriver): def list_instances_detail(self): infos = [] - for domain_id in self._conn.listDomainsID(): + for domain_id in self.list_instance_ids(): domain = self._conn.lookupByID(domain_id) info = self._map_to_instance_info(domain) infos.append(info) @@ -616,6 +629,13 @@ class LibvirtDriver(driver.ComputeDriver): raise exception.DeviceIsBusy(device=mount_device) raise + # TODO(danms) once libvirt has support for LXC hotplug, + # replace this re-define with use of the + # VIR_DOMAIN_AFFECT_LIVE & VIR_DOMAIN_AFFECT_CONFIG flags with + # attachDevice() + domxml = virt_dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) + self._conn.defineXML(domxml) + @staticmethod def _get_disk_xml(xml, device): """Returns the xml for the disk mounted at device""" @@ -650,6 +670,13 @@ class LibvirtDriver(driver.ComputeDriver): connection_info, mount_device) + # TODO(danms) once libvirt has support for LXC hotplug, + # replace this re-define with use of the + # VIR_DOMAIN_AFFECT_LIVE & VIR_DOMAIN_AFFECT_CONFIG flags with + # detachDevice() + domxml = virt_dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) + self._conn.defineXML(domxml) + @exception.wrap_exception() def _attach_lxc_volume(self, xml, virt_dom, instance_name): LOG.info(_('attaching LXC block device')) @@ -771,7 +798,9 @@ class LibvirtDriver(driver.ComputeDriver): libvirt_utils.create_snapshot(disk_path, snapshot_name) # Export the snapshot to a raw image - with utils.tempdir() as tmpdir: + snapshot_directory = FLAGS.libvirt_snapshots_directory + libvirt_utils.ensure_tree(snapshot_directory) + with utils.tempdir(dir=snapshot_directory) as tmpdir: try: out_path = os.path.join(tmpdir, snapshot_name) libvirt_utils.extract_snapshot(disk_path, source_format, @@ -1449,6 +1478,27 @@ class LibvirtDriver(driver.ComputeDriver): caps.parse_str(xmlstr) return caps + def get_host_cpu_for_guest(self): + """Returns an instance of config.LibvirtConfigGuestCPU + representing the host's CPU model & topology with + policy for configuring a guest to match""" + + caps = self.get_host_capabilities() + hostcpu = caps.host.cpu + guestcpu = config.LibvirtConfigGuestCPU() + + guestcpu.model = hostcpu.model + guestcpu.vendor = hostcpu.vendor + guestcpu.arch = hostcpu.arch + + guestcpu.match = "exact" + + for hostfeat in hostcpu.features: + guestfeat = config.LibvirtConfigGuestCPUFeature(hostfeat.name) + guestfeat.policy = "require" + + return guestcpu + def get_guest_cpu_config(self): mode = FLAGS.libvirt_cpu_mode model = FLAGS.libvirt_cpu_model @@ -1474,9 +1524,22 @@ class LibvirtDriver(driver.ComputeDriver): LOG.debug(_("CPU mode '%(mode)s' model '%(model)s' was chosen") % {'mode': mode, 'model': (model or "")}) - cpu = config.LibvirtConfigGuestCPU() - cpu.mode = mode - cpu.model = model + # TODO(berrange): in the future, when MIN_LIBVIRT_VERSION is + # updated to be at least this new, we can kill off the elif + # blocks here + if self.has_min_version(MIN_LIBVIRT_HOST_CPU_VERSION): + cpu = config.LibvirtConfigGuestCPU() + cpu.mode = mode + cpu.model = model + elif mode == "custom": + cpu = config.LibvirtConfigGuestCPU() + cpu.model = model + elif mode == "host-model": + cpu = self.get_host_cpu_for_guest() + elif mode == "host-passthrough": + msg = _("Passthrough of the host CPU was requested but " + "this libvirt version does not support this feature") + raise exception.NovaException(msg) return cpu @@ -1811,7 +1874,7 @@ class LibvirtDriver(driver.ComputeDriver): Return all block devices in use on this node. """ devices = [] - for dom_id in self._conn.listDomainsID(): + for dom_id in self.list_instance_ids(): domain = self._conn.lookupByID(dom_id) try: doc = etree.fromstring(domain.XMLDesc(0)) @@ -1925,7 +1988,7 @@ class LibvirtDriver(driver.ComputeDriver): """ total = 0 - for dom_id in self._conn.listDomainsID(): + for dom_id in self.list_instance_ids(): dom = self._conn.lookupByID(dom_id) vcpus = dom.vcpus() if vcpus is None: @@ -1952,7 +2015,7 @@ class LibvirtDriver(driver.ComputeDriver): idx3 = m.index('Cached:') if FLAGS.libvirt_type == 'xen': used = 0 - for domain_id in self._conn.listDomainsID(): + for domain_id in self.list_instance_ids(): # skip dom0 dom_mem = int(self._conn.lookupByID(domain_id).info()[2]) if domain_id != 0: diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py index 00823305b..b373a3b90 100644 --- a/nova/virt/libvirt/firewall.py +++ b/nova/virt/libvirt/firewall.py @@ -21,7 +21,7 @@ from eventlet import tpool from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging import nova.virt.firewall as base_firewall diff --git a/nova/virt/libvirt/imagecache.py b/nova/virt/libvirt/imagecache.py index 9e800e3dd..01c0046a0 100644 --- a/nova/virt/libvirt/imagecache.py +++ b/nova/virt/libvirt/imagecache.py @@ -32,8 +32,8 @@ from nova.compute import task_states from nova.compute import vm_states from nova import db from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.virt.libvirt import utils as virtutils diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py index 363c692aa..f08485ea3 100644 --- a/nova/virt/libvirt/utils.py +++ b/nova/virt/libvirt/utils.py @@ -26,9 +26,9 @@ import re from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import utils from nova.virt import images @@ -330,24 +330,6 @@ def file_delete(path): return os.unlink(path) -def get_open_port(start_port, end_port): - """Find an available port - - :param start_port: Start of acceptable port range - :param end_port: End of acceptable port range - """ - for i in xrange(0, 100): # don't loop forever - port = random.randint(start_port, end_port) - # netcat will exit with 0 only if the port is in use, - # so a nonzero return value implies it is unused - cmd = 'netcat', '0.0.0.0', port, '-w', '1' - try: - stdout, stderr = execute(*cmd, process_input='') - except exception.ProcessExecutionError: - return port - raise Exception(_('Unable to find an open port')) - - def get_fs_info(path): """Get free/used/total space info for a filesystem diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 3a93cf3c8..74c21fc51 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -21,9 +21,9 @@ from nova import exception from nova import flags -from nova import log as logging from nova.network import linux_net from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.virt import netutils from nova.virt import vif diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index 839a00db2..dfba9325c 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -22,7 +22,7 @@ import time from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils from nova.virt.libvirt import config diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 10f834ba1..fdf85dc8b 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -23,7 +23,7 @@ import pprint import uuid from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova.virt.vmwareapi import error_util _CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine', diff --git a/nova/virt/vmwareapi/io_util.py b/nova/virt/vmwareapi/io_util.py index d8dd46b2f..999e7a085 100644 --- a/nova/virt/vmwareapi/io_util.py +++ b/nova/virt/vmwareapi/io_util.py @@ -25,7 +25,7 @@ from eventlet import greenthread from eventlet import queue from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/virt/vmwareapi/network_utils.py b/nova/virt/vmwareapi/network_utils.py index 1f051fa7e..97d9d6c26 100644 --- a/nova/virt/vmwareapi/network_utils.py +++ b/nova/virt/vmwareapi/network_utils.py @@ -20,7 +20,7 @@ Utility functions for ESX Networking. """ from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova.virt.vmwareapi import error_util from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util diff --git a/nova/virt/vmwareapi/read_write_util.py b/nova/virt/vmwareapi/read_write_util.py index 08da01448..765b94cc6 100644 --- a/nova/virt/vmwareapi/read_write_util.py +++ b/nova/virt/vmwareapi/read_write_util.py @@ -30,7 +30,7 @@ import urlparse from glance import client from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging LOG = logging.getLogger(__name__) diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py index 52205c461..a00dd5c36 100644 --- a/nova/virt/vmwareapi/vif.py +++ b/nova/virt/vmwareapi/vif.py @@ -19,7 +19,7 @@ from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.virt import vif from nova.virt.vmwareapi import network_utils diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 000dc6981..f650316da 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -29,9 +29,9 @@ import uuid from nova.compute import power_state from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.virt.vmwareapi import network_utils from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py index c1ab77fa5..bb7219feb 100644 --- a/nova/virt/vmwareapi/vmware_images.py +++ b/nova/virt/vmwareapi/vmware_images.py @@ -21,7 +21,7 @@ import StringIO from nova import exception from nova.image import glance -from nova import log as logging +from nova.openstack.common import log as logging from nova.virt.vmwareapi import io_util from nova.virt.vmwareapi import read_write_util diff --git a/nova/virt/vmwareapi_conn.py b/nova/virt/vmwareapi_conn.py index 0c66791a6..2b1d4cedf 100644 --- a/nova/virt/vmwareapi_conn.py +++ b/nova/virt/vmwareapi_conn.py @@ -38,8 +38,8 @@ from eventlet import event from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.virt import driver from nova.virt.vmwareapi import error_util diff --git a/nova/virt/xenapi/connection.py b/nova/virt/xenapi/connection.py index 3dd7ad971..6db5e5b73 100644 --- a/nova/virt/xenapi/connection.py +++ b/nova/virt/xenapi/connection.py @@ -49,8 +49,8 @@ from nova import context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.virt import driver from nova.virt.xenapi import host from nova.virt.xenapi import pool @@ -504,7 +504,8 @@ class XenAPISession(object): """The session to invoke XenAPI SDK calls""" def __init__(self, url, user, pw): - self.XenAPI = self.get_imported_xenapi() + import XenAPI + self.XenAPI = XenAPI self._sessions = queue.Queue() self.is_slave = False exception = self.XenAPI.Failure(_("Unable to log in to XenAPI " @@ -562,10 +563,6 @@ class XenAPISession(object): product_version = software_version['product_version'] return tuple(int(part) for part in product_version.split('.')) - def get_imported_xenapi(self): - """Stubout point. This can be replaced with a mock xenapi module.""" - return __import__('XenAPI') - def get_session_id(self): """Return a string session_id. Used for vnc consoles.""" with self._get_session() as session: diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 2013e7184..72008a69d 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -58,8 +58,8 @@ from xml.sax import saxutils import pprint from nova import exception -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils @@ -498,13 +498,13 @@ class SessionBase(object): return as_json(returncode='0', message='success') elif (plugin, method) == ('agent', 'resetnetwork'): return as_json(returncode='0', message='success') - elif (plugin, method) == ('glance', 'copy_kernel_vdi'): - return '' elif (plugin, method) == ('glance', 'upload_vhd'): return '' - elif (plugin, method) == ('glance', 'create_kernel_ramdisk'): + elif (plugin, method) == ('kernel', 'copy_vdi'): + return '' + elif (plugin, method) == ('kernel', 'create_kernel_ramdisk'): return '' - elif (plugin, method) == ('glance', 'remove_kernel_ramdisk'): + elif (plugin, method) == ('kernel', 'remove_kernel_ramdisk'): return '' elif (plugin, method) == ('migration', 'move_vhds_into_sr'): return '' diff --git a/nova/virt/xenapi/firewall.py b/nova/virt/xenapi/firewall.py index 094da44eb..3c974fc0f 100644 --- a/nova/virt/xenapi/firewall.py +++ b/nova/virt/xenapi/firewall.py @@ -20,8 +20,8 @@ from nova import context from nova.db import api as db from nova import flags -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.virt import firewall from nova.virt import netutils diff --git a/nova/virt/xenapi/host.py b/nova/virt/xenapi/host.py index 3fe5dc292..6594600d5 100644 --- a/nova/virt/xenapi/host.py +++ b/nova/virt/xenapi/host.py @@ -38,7 +38,6 @@ class Host(object): Implements host related operations. """ def __init__(self, session): - self.XenAPI = session.get_imported_xenapi() self._session = session def host_power_action(self, _host, action): @@ -95,7 +94,7 @@ class Host(object): notifications.send_update(ctxt, old_ref, new_ref) break - except self.XenAPI.Failure: + except self._session.XenAPI.Failure: LOG.exception('Unable to migrate VM %(vm_ref)s' 'from %(host)s' % locals()) (old_ref, new_ref) = db.instance_update_and_get_original( @@ -178,7 +177,6 @@ def call_xenhost(session, method, arg_dict): out that behavior. """ # Create a task ID as something that won't match any instance ID - XenAPI = session.get_imported_xenapi() try: result = session.call_plugin('xenhost', method, args=arg_dict) if not result: @@ -187,7 +185,7 @@ def call_xenhost(session, method, arg_dict): except ValueError: LOG.exception(_("Unable to get updated status")) return None - except XenAPI.Failure as e: + except session.XenAPI.Failure as e: LOG.error(_("The call to %(method)s returned " "an error: %(e)s.") % locals()) return e.details[1] diff --git a/nova/virt/xenapi/pool.py b/nova/virt/xenapi/pool.py index 7b0c576bf..07a03d029 100644 --- a/nova/virt/xenapi/pool.py +++ b/nova/virt/xenapi/pool.py @@ -25,9 +25,9 @@ from nova.compute import aggregate_states from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.virt.xenapi import vm_utils @@ -48,7 +48,6 @@ class ResourcePool(object): Implements resource pool operations. """ def __init__(self, session): - self.XenAPI = session.get_imported_xenapi() host_ref = session.get_xenapi_host() host_rec = session.call_xenapi('host.get_record', host_ref) self._host_name = host_rec['hostname'] @@ -139,7 +138,7 @@ class ResourcePool(object): 'master_user': FLAGS.xenapi_connection_username, 'master_pass': FLAGS.xenapi_connection_password, } self._session.call_plugin('xenhost', 'host_join', args) - except self.XenAPI.Failure as e: + except self._session.XenAPI.Failure as e: LOG.error(_("Pool-Join failed: %(e)s") % locals()) raise exception.AggregateError(aggregate_id=aggregate_id, action='add_to_aggregate', @@ -158,7 +157,7 @@ class ResourcePool(object): host_ref = self._session.call_xenapi('host.get_by_uuid', host_uuid) self._session.call_xenapi("pool.eject", host_ref) - except self.XenAPI.Failure as e: + except self._session.XenAPI.Failure as e: LOG.error(_("Pool-eject failed: %(e)s") % locals()) raise exception.AggregateError(aggregate_id=aggregate_id, action='remove_from_aggregate', @@ -170,7 +169,7 @@ class ResourcePool(object): pool_ref = self._session.call_xenapi("pool.get_all")[0] self._session.call_xenapi("pool.set_name_label", pool_ref, aggregate_name) - except self.XenAPI.Failure as e: + except self._session.XenAPI.Failure as e: LOG.error(_("Unable to set up pool: %(e)s.") % locals()) raise exception.AggregateError(aggregate_id=aggregate_id, action='add_to_aggregate', @@ -181,7 +180,7 @@ class ResourcePool(object): try: pool_ref = self._session.call_xenapi('pool.get_all')[0] self._session.call_xenapi('pool.set_name_label', pool_ref, '') - except self.XenAPI.Failure as e: + except self._session.XenAPI.Failure as e: LOG.error(_("Pool-set_name_label failed: %(e)s") % locals()) raise exception.AggregateError(aggregate_id=aggregate_id, action='remove_from_aggregate', diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index 39778521b..4977fd4cf 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -20,8 +20,8 @@ """VIF drivers for XenAPI.""" from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.virt import vif from nova.virt.xenapi import network_utils from nova.virt.xenapi import vm_utils diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index cbc7a70ce..241b0da68 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -40,10 +40,10 @@ from nova import db from nova import exception from nova import flags from nova.image import glance -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import utils from nova.virt.disk import api as disk from nova.virt import xenapi @@ -454,7 +454,13 @@ def upload_image(context, session, instance, vdi_uuids, image_id): glance_host, glance_port = glance.pick_glance_api_server() + sys_meta = db.instance_system_metadata_get(context, instance['uuid']) properties = {} + prefix = 'image_' + for key, value in sys_meta.iteritems(): + if key.startswith(prefix): + key = key[len(prefix):] + properties[key] = value properties['auto_disk_config'] = instance.auto_disk_config properties['os_type'] = instance.os_type or FLAGS.default_os_type @@ -600,8 +606,8 @@ def create_kernel_image(context, session, instance, image_id, user_id, args = {} args['cached-image'] = image_id args['new-image-uuid'] = str(uuid.uuid4()) - filename = session.call_plugin('glance', 'create_kernel_ramdisk', - args) + filename = session.call_plugin( + 'kernel', 'create_kernel_ramdisk', args) if filename == "": return _fetch_image(context, session, instance, image_id, image_type) @@ -610,6 +616,15 @@ def create_kernel_image(context, session, instance, image_id, user_id, return {vdi_type: dict(uuid=None, file=filename)} +def destroy_kernel_ramdisk(session, kernel, ramdisk): + args = {} + if kernel: + args['kernel-file'] = kernel + if ramdisk: + args['ramdisk-file'] = ramdisk + session.call_plugin('kernel', 'remove_kernel_ramdisk', args) + + def _create_cached_image(context, session, instance, image_id, image_type): sr_ref = safe_find_sr(session) sr_type = session.call_xenapi('SR.get_record', sr_ref)["type"] @@ -904,7 +919,7 @@ def _fetch_disk_image(context, session, instance, image_id, image_type): # content of the VDI into the proper path. LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi_ref, instance=instance) - fn = "copy_kernel_vdi" + args = {} args['vdi-ref'] = vdi_ref @@ -912,7 +927,7 @@ def _fetch_disk_image(context, session, instance, image_id, image_type): args['image-size'] = str(vdi_size) if FLAGS.cache_images: args['cached-image'] = image_id - filename = session.call_plugin('glance', fn, args) + filename = session.call_plugin('kernel', 'copy_vdi', args) # Remove the VDI as it is not needed anymore. destroy_vdi(session, vdi_ref) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 475e8264f..6af2ac9e0 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -36,10 +36,10 @@ from nova import context as nova_context from nova import db from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import importutils from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils from nova.virt import driver @@ -152,7 +152,6 @@ class VMOps(object): Management class for VM-related tasks """ def __init__(self, session): - self.XenAPI = session.get_imported_xenapi() self.compute_api = compute.API() self._session = session self.poll_rescue_last_ran = None @@ -292,8 +291,9 @@ class VMOps(object): if kernel_file or ramdisk_file: LOG.debug(_("Removing kernel/ramdisk files from dom0"), instance=instance) - self._destroy_kernel_ramdisk_plugin_call(kernel_file, - ramdisk_file) + vm_utils.destroy_kernel_ramdisk( + self._session, kernel_file, ramdisk_file) + undo_mgr.undo_with(undo_create_kernel_ramdisk) return kernel_file, ramdisk_file @@ -618,7 +618,7 @@ class VMOps(object): template_vm_ref, template_vdi_uuids = vm_utils.create_snapshot( self._session, instance, vm_ref, label) return template_vm_ref, template_vdi_uuids - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.error(_("Unable to Snapshot instance: %(exc)s"), locals(), instance=instance) raise @@ -634,7 +634,7 @@ class VMOps(object): _params = {'params': pickle.dumps(params)} self._session.call_plugin('migration', 'transfer_vhd', _params) - except self.XenAPI.Failure: + except self._session.XenAPI.Failure: msg = _("Failed to transfer vhd to new host") raise exception.MigrationError(reason=msg) @@ -976,7 +976,7 @@ class VMOps(object): self._session.call_xenapi('VM.hard_shutdown', vm_ref) else: self._session.call_xenapi('VM.clean_shutdown', vm_ref) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) def _find_root_vdi_ref(self, vm_ref): @@ -1001,14 +1001,6 @@ class VMOps(object): except volume_utils.StorageError as exc: LOG.error(exc) - def _destroy_kernel_ramdisk_plugin_call(self, kernel, ramdisk): - args = {} - if kernel: - args['kernel-file'] = kernel - if ramdisk: - args['ramdisk-file'] = ramdisk - self._session.call_plugin('glance', 'remove_kernel_ramdisk', args) - def _destroy_kernel_ramdisk(self, instance, vm_ref): """Three situations can occur: @@ -1038,14 +1030,14 @@ class VMOps(object): (kernel, ramdisk) = vm_utils.lookup_kernel_ramdisk(self._session, vm_ref) - self._destroy_kernel_ramdisk_plugin_call(kernel, ramdisk) + vm_utils.destroy_kernel_ramdisk(self._session, kernel, ramdisk) LOG.debug(_("kernel/ramdisk files removed"), instance=instance) def _destroy_vm(self, instance, vm_ref): """Destroys a VM record.""" try: self._session.call_xenapi('VM.destroy', vm_ref) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) return @@ -1499,7 +1491,7 @@ class VMOps(object): args.update(addl_args) try: return self._session.call_plugin(plugin, method, args) - except self.XenAPI.Failure, e: + except self._session.XenAPI.Failure, e: err_msg = e.details[-1].splitlines()[-1] if 'TIMEOUT:' in err_msg: LOG.error(_('TIMEOUT: The call to %(method)s timed out. ' diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py index 143970cfb..d64394980 100644 --- a/nova/virt/xenapi/volume_utils.py +++ b/nova/virt/xenapi/volume_utils.py @@ -23,7 +23,7 @@ import re import string from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py index c90df787b..587c5877a 100644 --- a/nova/virt/xenapi/volumeops.py +++ b/nova/virt/xenapi/volumeops.py @@ -19,7 +19,7 @@ Management class for Storage-related functions (attach, detach, etc). """ from nova import exception -from nova import log as logging +from nova.openstack.common import log as logging from nova.virt.xenapi import vm_utils from nova.virt.xenapi import volume_utils @@ -33,7 +33,6 @@ class VolumeOps(object): """ def __init__(self, session): - self.XenAPI = session.get_imported_xenapi() self._session = session def create_volume_for_sm(self, volume, sr_uuid): @@ -42,7 +41,7 @@ class VolumeOps(object): sm_vol_rec = {} try: sr_ref = self._session.call_xenapi("SR.get_by_uuid", sr_uuid) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) raise volume_utils.StorageError(_('Unable to get SR using uuid')) #Create VDI @@ -149,7 +148,7 @@ class VolumeOps(object): try: sr_ref = self.introduce_sr(uuid, label, sr_params) LOG.debug(_('Introduced %(label)s as %(sr_ref)s.') % locals()) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) raise volume_utils.StorageError( _('Unable to introduce Storage Repository')) @@ -177,7 +176,7 @@ class VolumeOps(object): try: vbd_ref = vm_utils.create_vbd(self._session, vm_ref, vdi_ref, dev_number, bootable=False) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) self.forget_sr(uuid) raise Exception(_('Unable to use SR %(sr_ref)s for' @@ -185,7 +184,7 @@ class VolumeOps(object): try: self._session.call_xenapi("VBD.plug", vbd_ref) - except self.XenAPI.Failure, exc: + except self._session.XenAPI.Failure, exc: LOG.exception(exc) self.forget_sr(uuid) raise Exception(_('Unable to attach volume to instance %s') diff --git a/nova/vnc/xvp_proxy.py b/nova/vnc/xvp_proxy.py index ecf5c2551..c8b779a37 100644 --- a/nova/vnc/xvp_proxy.py +++ b/nova/vnc/xvp_proxy.py @@ -29,8 +29,8 @@ import eventlet.wsgi from nova.consoleauth import rpcapi as consoleauth_rpcapi from nova import context from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import version from nova import wsgi diff --git a/nova/volume/__init__.py b/nova/volume/__init__.py index 1db8efbfd..3fb070052 100644 --- a/nova/volume/__init__.py +++ b/nova/volume/__init__.py @@ -21,5 +21,8 @@ import nova.flags import nova.openstack.common.importutils -API = nova.openstack.common.importutils.import_class( - nova.flags.FLAGS.volume_api_class) + +def API(): + importutils = nova.openstack.common.importutils + cls = importutils.import_class(nova.flags.FLAGS.volume_api_class) + return cls() diff --git a/nova/volume/api.py b/nova/volume/api.py index 748b2d16b..28f78a7e1 100644 --- a/nova/volume/api.py +++ b/nova/volume/api.py @@ -25,7 +25,7 @@ import functools from nova.db import base from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova.openstack.common import rpc from nova.openstack.common import timeutils import nova.policy diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py new file mode 100644 index 000000000..0950f4113 --- /dev/null +++ b/nova/volume/cinder.py @@ -0,0 +1,229 @@ +# 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. + +""" +Handles all requests relating to volumes + cinder. +""" + + +from cinderclient import service_catalog +from cinderclient.v1 import client as cinder_client + +from nova.db import base +from nova import exception +from nova import flags +from nova.openstack.common import log as logging + +FLAGS = flags.FLAGS + +LOG = logging.getLogger(__name__) + + +def cinderclient(context): + + # FIXME: the cinderclient ServiceCatalog object is mis-named. + # It actually contains the entire access blob. + compat_catalog = { + 'access': {'serviceCatalog': context.service_catalog} + } + sc = service_catalog.ServiceCatalog(compat_catalog) + url = sc.url_for(service_type='volume', service_name='cinder') + + LOG.debug('cinderclient connection created using token "%s" and url "%s"' % + (context.auth_token, url)) + + c = cinder_client.Client(context.user_id, + context.auth_token, + project_id=context.project_id, + auth_url=url) + c.client.auth_token = context.auth_token + c.client.management_url = url + return c + + +def _untranslate_volume_summary_view(context, vol): + """Maps keys for volumes summary view.""" + d = {} + + d['id'] = vol.id + d['status'] = vol.status + d['size'] = vol.size + d['availability_zone'] = vol.availability_zone + d['created_at'] = vol.created_at + + # TODO(jdg): The calling code expects attach_time and + # mountpoint to be set. When the calling + # code is more defensive this can be + # removed. + d['attach_time'] = "" + d['mountpoint'] = "" + + if vol.attachments: + att = vol.attachments[0] + d['attach_status'] = 'attached' + d['instance_uuid'] = att['server_id'] + d['mountpoint'] = att['device'] + else: + d['attach_status'] = 'detached' + + d['display_name'] = vol.display_name + d['display_description'] = vol.display_description + + # TODO(jdg): Information may be lost in this translation + d['volume_type_id'] = vol.volume_type + d['snapshot_id'] = vol.snapshot_id + + d['vol_metadata'] = [] + for k, v in vol.metadata: + item = {} + item['key'] = k + item['value'] = v + d['vol_metadata'].append(item) + + return d + + +def _untranslate_snapshot_summary_view(context, snapshot): + """Maps keys for snapshots summary view.""" + d = {} + + d['id'] = snapshot.id + d['status'] = snapshot.status + d['progress'] = snapshot.progress + d['size'] = snapshot.size + d['created_at'] = snapshot.created_at + d['display_name'] = snapshot.display_name + d['display_description'] = snapshot.display_description + d['volume_id'] = snapshot.volume_id + d['project_id'] = snapshot.project_id + d['volume_size'] = snapshot.size + + return d + + +class API(base.Base): + """API for interacting with the volume manager.""" + + def get(self, context, volume_id): + item = cinderclient(context).volumes.get(volume_id) + return _untranslate_volume_summary_view(context, item) + + def get_all(self, context, search_opts={}): + items = cinderclient(context).volumes.list(detailed=True) + rval = [] + + for item in items: + rval.append(_untranslate_volume_summary_view(context, item)) + + return rval + + def check_attach(self, context, volume): + # TODO(vish): abstract status checking? + if volume['status'] != "available": + msg = _("status must be available") + raise exception.InvalidVolume(reason=msg) + if volume['attach_status'] == "attached": + msg = _("already attached") + raise exception.InvalidVolume(reason=msg) + + def check_detach(self, context, volume): + # TODO(vish): abstract status checking? + if volume['status'] == "available": + msg = _("already detached") + raise exception.InvalidVolume(reason=msg) + + def reserve_volume(self, context, volume): + cinderclient(context).volumes.reserve(volume['id']) + + def unreserve_volume(self, context, volume): + cinderclient(context).volumes.reserve(volume['id']) + + def attach(self, context, volume, instance_uuid, mountpoint): + cinderclient(context).volumes.attach(volume['id'], + instance_uuid, + mountpoint) + + def detach(self, context, volume): + cinderclient(context).volumes.detach(volume['id']) + + def initialize_connection(self, context, volume, connector): + return cinderclient(context).\ + volumes.initialize_connection(volume['id'], connector) + + def terminate_connection(self, context, volume, connector): + return cinderclient(context).\ + volumes.terminate_connection(volume['id'], connector) + + def create(self, context, size, name, description, snapshot=None, + volume_type=None, metadata=None, availability_zone=None): + + item = cinderclient(context).volumes.create(size, snapshot, + name, description, + volume_type) + + volume = _untranslate_volume_summary_view(context, item) + return _untranslate_volume_summary_view(context, item) + + def delete(self, context, volume): + cinderclient(context).volumes.delete(volume['id']) + + def update(self, context, volume, fields): + raise NotImplementedError() + + def get_snapshot(self, context, snapshot_id): + item = cinderclient(context).volume_snapshots.get(snapshot_id) + return _untranslate_snapshot_summary_view(context, item) + + def get_all_snapshots(self, context): + items = cinderclient(context).volume_snapshots.list(detailed=True) + rvals = [] + + for item in items: + rvals.append(_untranslate_snapshot_summary_view(context, item)) + + return rvals + + def create_snapshot(self, context, volume, name, description): + item = cinderclient(context).volume_snapshots.create(volume['id'], + False, + name, + description) + return _untranslate_snapshot_summary_view(context, item) + + def create_snapshot_force(self, context, volume, name, description): + item = cinderclient(context).volume_snapshots.create(volume['id'], + True, + name, + description) + + return _untranslate_snapshot_summary_view(context, item) + + def delete_snapshot(self, context, snapshot): + cinderclient(context).volume_snapshots.delete(snapshot['id']) + + def get_volume_metadata(self, context, volume): + raise NotImplementedError() + + def delete_volume_metadata(self, context, volume, key): + raise NotImplementedError() + + def update_volume_metadata(self, context, volume, metadata, delete=False): + raise NotImplementedError() + + def get_volume_metadata_value(self, volume, key): + raise NotImplementedError() diff --git a/nova/volume/driver.py b/nova/volume/driver.py index 2b0d457e5..9e8355a3a 100644 --- a/nova/volume/driver.py +++ b/nova/volume/driver.py @@ -24,8 +24,8 @@ import time from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova import utils from nova.volume import iscsi diff --git a/nova/volume/manager.py b/nova/volume/manager.py index 972c4b5ec..8aceace5f 100644 --- a/nova/volume/manager.py +++ b/nova/volume/manager.py @@ -41,11 +41,11 @@ intact. from nova import context from nova import exception from nova import flags -from nova import log as logging from nova import manager from nova.openstack.common import cfg from nova.openstack.common import excutils from nova.openstack.common import importutils +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import quota from nova import utils diff --git a/nova/volume/netapp.py b/nova/volume/netapp.py index 897da6204..57c1b8db4 100644 --- a/nova/volume/netapp.py +++ b/nova/volume/netapp.py @@ -32,8 +32,8 @@ from suds.sax import text from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.volume import driver LOG = logging.getLogger(__name__) diff --git a/nova/volume/nexenta/jsonrpc.py b/nova/volume/nexenta/jsonrpc.py index c0665e3dc..e0d9c810a 100644 --- a/nova/volume/nexenta/jsonrpc.py +++ b/nova/volume/nexenta/jsonrpc.py @@ -24,8 +24,8 @@ import urllib2 -from nova import log as logging from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova.volume import nexenta LOG = logging.getLogger(__name__) diff --git a/nova/volume/nexenta/volume.py b/nova/volume/nexenta/volume.py index 784d48d4f..9bb6364a9 100644 --- a/nova/volume/nexenta/volume.py +++ b/nova/volume/nexenta/volume.py @@ -24,8 +24,8 @@ from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg +from nova.openstack.common import log as logging from nova.volume import driver from nova.volume import nexenta from nova.volume.nexenta import jsonrpc diff --git a/nova/volume/san.py b/nova/volume/san.py index e1fa2c2d3..a2b79c298 100644 --- a/nova/volume/san.py +++ b/nova/volume/san.py @@ -34,9 +34,9 @@ from lxml import etree from nova import exception from nova import flags -from nova import log as logging from nova.openstack.common import cfg from nova.openstack.common import jsonutils +from nova.openstack.common import log as logging from nova import utils import nova.volume.driver diff --git a/nova/volume/utils.py b/nova/volume/utils.py index 7efd7f23a..dcaa141f5 100644 --- a/nova/volume/utils.py +++ b/nova/volume/utils.py @@ -17,8 +17,8 @@ """Volume-related Utilities and helpers.""" from nova import flags -from nova import log as logging from nova.notifier import api as notifier_api +from nova.openstack.common import log as logging from nova.openstack.common import timeutils from nova import utils diff --git a/nova/volume/volume_types.py b/nova/volume/volume_types.py index 0b8208663..67d824465 100644 --- a/nova/volume/volume_types.py +++ b/nova/volume/volume_types.py @@ -25,7 +25,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.openstack.common import log as logging FLAGS = flags.FLAGS LOG = logging.getLogger(__name__) diff --git a/nova/volume/xensm.py b/nova/volume/xensm.py index e834a312a..db3a1d793 100644 --- a/nova/volume/xensm.py +++ b/nova/volume/xensm.py @@ -14,7 +14,7 @@ from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging from nova import utils from nova.virt.xenapi import connection as xenapi_conn from nova.virt.xenapi import volumeops diff --git a/nova/wsgi.py b/nova/wsgi.py index a02fdcc18..afb5303e4 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -32,7 +32,7 @@ import webob.exc from nova import exception from nova import flags -from nova import log as logging +from nova.openstack.common import log as logging FLAGS = flags.FLAGS diff --git a/openstack-common.conf b/openstack-common.conf index efbecba3b..4eb15e0f2 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -1,7 +1,7 @@ [DEFAULT] # The list of modules to copy from openstack-common -modules=cfg,excutils,importutils,iniparser,jsonutils,local,policy,setup,timeutils,rpc +modules=cfg,excutils,gettextutils,importutils,iniparser,jsonutils,local,log,policy,setup,timeutils,rpc # The base module to hold the copy of openstack.common base=nova diff --git a/plugins/xenserver/xenapi/contrib/rpmbuild/SPECS/openstack-xen-plugins.spec b/plugins/xenserver/xenapi/contrib/rpmbuild/SPECS/openstack-xen-plugins.spec index 505d42fe2..8f4415311 100644 --- a/plugins/xenserver/xenapi/contrib/rpmbuild/SPECS/openstack-xen-plugins.spec +++ b/plugins/xenserver/xenapi/contrib/rpmbuild/SPECS/openstack-xen-plugins.spec @@ -30,6 +30,7 @@ rm -rf $RPM_BUILD_ROOT %defattr(-,root,root,-) /etc/xapi.d/plugins/agent /etc/xapi.d/plugins/glance +/etc/xapi.d/plugins/kernel /etc/xapi.d/plugins/migration /etc/xapi.d/plugins/pluginlib_nova.py /etc/xapi.d/plugins/xenhost diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance index 58de40a01..6fbd14714 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance @@ -28,7 +28,6 @@ except ImportError: import simplejson as json import md5 import os -import os.path import shutil import urllib2 @@ -40,47 +39,11 @@ import utils from pluginlib_nova import * configure_logging('glance') -KERNEL_DIR = '/boot/guest' - class RetryableError(Exception): pass -def _copy_kernel_vdi(dest, copy_args): - vdi_uuid = copy_args['vdi_uuid'] - vdi_size = copy_args['vdi_size'] - cached_image = copy_args['cached-image'] - logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", - dest, vdi_uuid) - filename = KERNEL_DIR + '/' + vdi_uuid - #make sure KERNEL_DIR exists, otherwise create it - if not os.path.isdir(KERNEL_DIR): - logging.debug("Creating directory %s", KERNEL_DIR) - os.makedirs(KERNEL_DIR) - #read data from /dev/ and write into a file on /boot/guest - of = open(filename, 'wb') - f = open(dest, 'rb') - #copy only vdi_size bytes - data = f.read(vdi_size) - of.write(data) - if cached_image: - #create a cache file. If caching is enabled, kernel images do not have - #to be fetched from glance. - cached_image = KERNEL_DIR + '/' + cached_image - logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", - dest, cached_image) - cache_file = open(cached_image, 'wb') - cache_file.write(data) - cache_file.close() - logging.debug("Done. Filename: %s", cached_image) - - f.close() - of.close() - logging.debug("Done. Filename: %s", filename) - return filename - - def _download_tarball_and_verify(request, staging_path): try: response = urllib2.urlopen(request) @@ -225,24 +188,6 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, conn.close() -def create_kernel_ramdisk(session, args): - """Creates a copy of the kernel/ramdisk image if it is present in the - cache. If the image is not present in the cache, it does nothing. - """ - cached_image = exists(args, 'cached-image') - image_uuid = exists(args, 'new-image-uuid') - cached_image_filename = KERNEL_DIR + '/' + cached_image - filename = KERNEL_DIR + '/' + image_uuid - - if os.path.isfile(cached_image_filename): - shutil.copyfile(cached_image_filename, filename) - logging.debug("Done. Filename: %s", filename) - else: - filename = "" - logging.debug("Cached kernel/ramdisk image not found") - return filename - - def download_vhd(session, args): """Download an image from Glance, unbundle it, and then deposit the VHDs into the storage repository @@ -295,35 +240,6 @@ def upload_vhd(session, args): return "" # Nothing useful to return on an upload -def copy_kernel_vdi(session, args): - vdi = exists(args, 'vdi-ref') - size = exists(args, 'image-size') - cached_image = optional(args, 'cached-image') - #Use the uuid as a filename - vdi_uuid = session.xenapi.VDI.get_uuid(vdi) - copy_args = {'vdi_uuid': vdi_uuid, - 'vdi_size': int(size), - 'cached-image': cached_image} - filename = with_vdi_in_dom0(session, vdi, False, - lambda dev: - _copy_kernel_vdi('/dev/%s' % dev, copy_args)) - return filename - - -def remove_kernel_ramdisk(session, args): - """Removes kernel and/or ramdisk from dom0's file system""" - kernel_file = optional(args, 'kernel-file') - ramdisk_file = optional(args, 'ramdisk-file') - if kernel_file: - os.remove(kernel_file) - if ramdisk_file: - os.remove(ramdisk_file) - return "ok" - - if __name__ == '__main__': XenAPIPlugin.dispatch({'upload_vhd': upload_vhd, - 'download_vhd': download_vhd, - 'copy_kernel_vdi': copy_kernel_vdi, - 'create_kernel_ramdisk': create_kernel_ramdisk, - 'remove_kernel_ramdisk': remove_kernel_ramdisk}) + 'download_vhd': download_vhd}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/kernel b/plugins/xenserver/xenapi/etc/xapi.d/plugins/kernel new file mode 100755 index 000000000..a0ca7badc --- /dev/null +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/kernel @@ -0,0 +1,123 @@ +#!/usr/bin/env python + +# Copyright (c) 2012 Openstack, LLC +# Copyright (c) 2010 Citrix Systems, Inc. +# 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. + +"""Handle the manipulation of kernel images.""" + +import os +import shutil + +import XenAPIPlugin + +#FIXME(sirp): should this use pluginlib from 5.6? +from pluginlib_nova import * +configure_logging('kernel') + +KERNEL_DIR = '/boot/guest' + + +def _copy_vdi(dest, copy_args): + vdi_uuid = copy_args['vdi_uuid'] + vdi_size = copy_args['vdi_size'] + cached_image = copy_args['cached-image'] + + logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", + dest, vdi_uuid) + filename = KERNEL_DIR + '/' + vdi_uuid + + # Make sure KERNEL_DIR exists, otherwise create it + if not os.path.isdir(KERNEL_DIR): + logging.debug("Creating directory %s", KERNEL_DIR) + os.makedirs(KERNEL_DIR) + + # Read data from /dev/ and write into a file on /boot/guest + of = open(filename, 'wb') + f = open(dest, 'rb') + + # Copy only vdi_size bytes + data = f.read(vdi_size) + of.write(data) + + if cached_image: + # Create a cache file. If caching is enabled, kernel images do not have + # to be fetched from glance. + cached_image = KERNEL_DIR + '/' + cached_image + logging.debug("copying kernel/ramdisk file from %s to /boot/guest/%s", + dest, cached_image) + cache_file = open(cached_image, 'wb') + cache_file.write(data) + cache_file.close() + logging.debug("Done. Filename: %s", cached_image) + + f.close() + of.close() + logging.debug("Done. Filename: %s", filename) + return filename + + +def copy_vdi(session, args): + vdi = exists(args, 'vdi-ref') + size = exists(args, 'image-size') + cached_image = optional(args, 'cached-image') + + # Use the uuid as a filename + vdi_uuid = session.xenapi.VDI.get_uuid(vdi) + copy_args = {'vdi_uuid': vdi_uuid, + 'vdi_size': int(size), + 'cached-image': cached_image} + + filename = with_vdi_in_dom0(session, vdi, False, + lambda dev: + _copy_vdi('/dev/%s' % dev, copy_args)) + return filename + + +def create_kernel_ramdisk(session, args): + """Creates a copy of the kernel/ramdisk image if it is present in the + cache. If the image is not present in the cache, it does nothing. + """ + cached_image = exists(args, 'cached-image') + image_uuid = exists(args, 'new-image-uuid') + cached_image_filename = KERNEL_DIR + '/' + cached_image + filename = KERNEL_DIR + '/' + image_uuid + + if os.path.isfile(cached_image_filename): + shutil.copyfile(cached_image_filename, filename) + logging.debug("Done. Filename: %s", filename) + else: + filename = "" + logging.debug("Cached kernel/ramdisk image not found") + return filename + + +def remove_kernel_ramdisk(session, args): + """Removes kernel and/or ramdisk from dom0's file system""" + kernel_file = optional(args, 'kernel-file') + ramdisk_file = optional(args, 'ramdisk-file') + if kernel_file: + os.remove(kernel_file) + if ramdisk_file: + os.remove(ramdisk_file) + return "ok" + + +if __name__ == '__main__': + XenAPIPlugin.dispatch({'copy_vdi': copy_vdi, + 'create_kernel_ramdisk': create_kernel_ramdisk, + 'remove_kernel_ramdisk': remove_kernel_ramdisk}) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py index 7e27b4ff7..5cfd32dbd 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenstore.py @@ -26,6 +26,7 @@ try: import json except ImportError: import simplejson as json + import logging import os import subprocess diff --git a/smoketests/base.py b/smoketests/base.py index db28b6f5a..aa0a0aae2 100644 --- a/smoketests/base.py +++ b/smoketests/base.py @@ -17,6 +17,7 @@ # under the License. import boto +from boto.ec2.regioninfo import RegionInfo import commands import httplib import os @@ -24,7 +25,6 @@ import paramiko import sys import time import unittest -from boto.ec2.regioninfo import RegionInfo from smoketests import flags @@ -150,7 +150,7 @@ class SmokeTestCase(unittest.TestCase): def create_key_pair(self, conn, key_name): try: os.remove('/tmp/%s.pem' % key_name) - except: + except Exception: pass key = conn.create_key_pair(key_name) key.save('/tmp/') @@ -160,7 +160,7 @@ class SmokeTestCase(unittest.TestCase): conn.delete_key_pair(key_name) try: os.remove('/tmp/%s.pem' % key_name) - except: + except Exception: pass def bundle_image(self, image, tempdir='/tmp', kernel=False): diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py index 5e1d7aaa9..4fb843e0f 100644 --- a/smoketests/public_network_smoketests.py +++ b/smoketests/public_network_smoketests.py @@ -30,8 +30,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), 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 +from smoketests import flags #Note that this test should run from #public network (outside of private network segments) diff --git a/smoketests/run_tests.py b/smoketests/run_tests.py index 30592b6a3..053acc09f 100644 --- a/smoketests/run_tests.py +++ b/smoketests/run_tests.py @@ -57,8 +57,8 @@ To run a single test module: import gettext import os -import unittest import sys +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... @@ -110,7 +110,7 @@ class _AnsiColorizer(object): except curses.error: curses.setupterm() return curses.tigetnum("colors") > 2 - except: + except Exception: raise # guess false in case of error return False @@ -133,9 +133,13 @@ class _Win32Colorizer(object): See _AnsiColorizer docstring. """ def __init__(self, stream): - from win32console import (GetStdHandle, STD_OUT_HANDLE, - FOREGROUND_RED, FOREGROUND_GREEN, - FOREGROUND_BLUE, FOREGROUND_INTENSITY) + from win32console import FOREGROUND_BLUE + from win32console import FOREGROUND_GREEN + from win32console import FOREGROUND_INTENSITY + from win32console import FOREGROUND_RED + from win32console import GetStdHandle + from win32console import STD_OUT_HANDLE + red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE, FOREGROUND_INTENSITY) self.stream = stream diff --git a/smoketests/test_netadmin.py b/smoketests/test_netadmin.py index 6a0dc48ec..4215f705d 100644 --- a/smoketests/test_netadmin.py +++ b/smoketests/test_netadmin.py @@ -30,8 +30,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), 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 +from smoketests import flags FLAGS = flags.FLAGS diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py index 5e599d15b..8414a755f 100644 --- a/smoketests/test_sysadmin.py +++ b/smoketests/test_sysadmin.py @@ -18,10 +18,10 @@ import os import random +import shutil import sys -import time import tempfile -import shutil +import time # 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... @@ -31,8 +31,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]), 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 +from smoketests import flags FLAGS = flags.FLAGS flags.DEFINE_string('bundle_kernel', 'random.kernel', diff --git a/tools/clean_file_locks.py b/tools/clean_file_locks.py index 790d82f67..39b7d695b 100755 --- a/tools/clean_file_locks.py +++ b/tools/clean_file_locks.py @@ -26,7 +26,7 @@ import logging import optparse from nova import flags -from nova import log +from nova.openstack.common import log from nova import utils diff --git a/tools/hacking.py b/tools/hacking.py index 93e51e66c..b924167f5 100755 --- a/tools/hacking.py +++ b/tools/hacking.py @@ -275,7 +275,7 @@ class LocalizationError(Exception): pass -def check_l18n(): +def check_i18n(): """Generator that checks token stream for localization errors. Expects tokens to be ``send``ed one by one. @@ -340,7 +340,7 @@ def nova_localization_strings(logical_line, tokens): N703: multiple positional placeholders """ - gen = check_l18n() + gen = check_i18n() next(gen) try: map(gen.send, tokens) diff --git a/tools/pip-requires b/tools/pip-requires index 234c31a7b..010289137 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -24,3 +24,4 @@ Babel>=0.9.6 iso8601>=0.1.4 httplib2 setuptools_git>=0.4 +python-quantumclient>=0.1,<0.2 @@ -18,7 +18,7 @@ downloadcache = ~/cache/pip [testenv:pep8] deps = pep8==1.1 -commands = pep8 --repeat --show-source --exclude=.venv,.tox,dist,doc,*egg . +commands = python tools/hacking.py --ignore=N4 --repeat --show-source --exclude=.venv,.tox,dist,doc,*egg . [testenv:cover] setenv = NOSE_WITH_COVERAGE=1 |