Fixing merge conflicts with new branch
This commit is contained in:
@@ -6,6 +6,7 @@ keys
|
||||
networks
|
||||
nova.sqlite
|
||||
CA/cacert.pem
|
||||
CA/crl.pem
|
||||
CA/index.txt*
|
||||
CA/openssl.cnf
|
||||
CA/serial*
|
||||
|
||||
3
.mailmap
3
.mailmap
@@ -27,3 +27,6 @@
|
||||
<vishvananda@gmail.com> <root@ubuntu>
|
||||
<sleepsonthefloor@gmail.com> <root@tonbuntu>
|
||||
<rlane@wikimedia.org> <laner@controller>
|
||||
<rconradharris@gmail.com> <rick.harris@rackspace.com>
|
||||
<corywright@gmail.com> <cory.wright@rackspace.com>
|
||||
<ant@openstack.org> <amesserl@rackspace.com>
|
||||
|
||||
6
Authors
6
Authors
@@ -1,9 +1,12 @@
|
||||
Andy Smith <code@term.ie>
|
||||
Anne Gentle <anne@openstack.org>
|
||||
Anthony Young <sleepsonthefloor@gmail.com>
|
||||
Antony Messerli <ant@openstack.org>
|
||||
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
|
||||
Chris Behrens <cbehrens@codestud.com>
|
||||
Chmouel Boudjnah <chmouel@chmouel.com>
|
||||
Cory Wright <corywright@gmail.com>
|
||||
David Pravec <David.Pravec@danix.org>
|
||||
Dean Troyer <dtroyer@gmail.com>
|
||||
Devin Carlen <devin.carlen@gmail.com>
|
||||
Ed Leafe <ed@leafe.com>
|
||||
@@ -24,8 +27,10 @@ Michael Gundlach <michael.gundlach@rackspace.com>
|
||||
Monty Taylor <mordred@inaugust.com>
|
||||
Paul Voccio <paul@openstack.org>
|
||||
Rick Clark <rick@openstack.org>
|
||||
Rick Harris <rconradharris@gmail.com>
|
||||
Ryan Lane <rlane@wikimedia.org>
|
||||
Ryan Lucio <rlucio@internap.com>
|
||||
Salvatore Orlando <salvatore.orlando@eu.citrix.com>
|
||||
Sandy Walsh <sandy.walsh@rackspace.com>
|
||||
Soren Hansen <soren.hansen@rackspace.com>
|
||||
Thierry Carrez <thierry@openstack.org>
|
||||
@@ -34,3 +39,4 @@ Trey Morris <trey.morris@rackspace.com>
|
||||
Vishvananda Ishaya <vishvananda@gmail.com>
|
||||
Youcef Laribi <Youcef.Laribi@eu.citrix.com>
|
||||
Zhixue Wu <Zhixue.Wu@citrix.com>
|
||||
|
||||
|
||||
109
bin/nova-api-paste
Executable file
109
bin/nova-api-paste
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python
|
||||
# pylint: disable-msg=C0103
|
||||
# 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.
|
||||
|
||||
"""Starter script for Nova API."""
|
||||
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from paste import deploy
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
from nova import flags
|
||||
from nova import wsgi
|
||||
|
||||
LOG = logging.getLogger('nova.api')
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
LOG.addHandler(logging.StreamHandler())
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
API_ENDPOINTS = ['ec2', 'openstack']
|
||||
|
||||
|
||||
def load_configuration(paste_config):
|
||||
"""Load the paste configuration from the config file and return it."""
|
||||
config = None
|
||||
# Try each known name to get the global DEFAULTS, which will give ports
|
||||
for name in API_ENDPOINTS:
|
||||
try:
|
||||
config = deploy.appconfig("config:%s" % paste_config, name=name)
|
||||
except LookupError:
|
||||
pass
|
||||
if config:
|
||||
verbose = config.get('verbose', None)
|
||||
if verbose:
|
||||
FLAGS.verbose = int(verbose) == 1
|
||||
if FLAGS.verbose:
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
return config
|
||||
LOG.debug(_("Paste config at %s has no secion for known apis"),
|
||||
paste_config)
|
||||
print _("Paste config at %s has no secion for any known apis") % \
|
||||
paste_config
|
||||
os.exit(1)
|
||||
|
||||
|
||||
def launch_api(paste_config_file, section, server, port, host):
|
||||
"""Launch an api server from the specified port and IP."""
|
||||
LOG.debug(_("Launching %s api on %s:%s"), section, host, port)
|
||||
app = deploy.loadapp('config:%s' % paste_config_file, name=section)
|
||||
server.start(app, int(port), host)
|
||||
|
||||
|
||||
def run_app(paste_config_file):
|
||||
LOG.debug(_("Using paste.deploy config at: %s"), configfile)
|
||||
config = load_configuration(paste_config_file)
|
||||
LOG.debug(_("Configuration: %r"), config)
|
||||
server = wsgi.Server()
|
||||
ip = config.get('host', '0.0.0.0')
|
||||
for api in API_ENDPOINTS:
|
||||
port = config.get("%s_port" % api, None)
|
||||
if not port:
|
||||
continue
|
||||
host = config.get("%s_host" % api, ip)
|
||||
launch_api(configfile, api, server, port, host)
|
||||
LOG.debug(_("All api servers launched, now waiting"))
|
||||
server.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
FLAGS(sys.argv)
|
||||
configfiles = ['/etc/nova/nova-api.conf']
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
configfiles.insert(0,
|
||||
os.path.join(possible_topdir, 'etc', 'nova-api.conf'))
|
||||
for configfile in configfiles:
|
||||
if os.path.exists(configfile):
|
||||
run_app(configfile)
|
||||
break
|
||||
else:
|
||||
LOG.debug(_("Skipping missing configuration: %s"), configfile)
|
||||
@@ -53,6 +53,7 @@
|
||||
CLI interface for nova management.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import gettext
|
||||
import logging
|
||||
import os
|
||||
@@ -452,6 +453,52 @@ class NetworkCommands(object):
|
||||
int(network_size), int(vlan_start),
|
||||
int(vpn_start))
|
||||
|
||||
|
||||
class ServiceCommands(object):
|
||||
"""Enable and disable running services"""
|
||||
|
||||
def list(self, host=None, service=None):
|
||||
"""Show a list of all running services. Filter by host & service name.
|
||||
args: [host] [service]"""
|
||||
ctxt = context.get_admin_context()
|
||||
now = datetime.datetime.utcnow()
|
||||
services = db.service_get_all(ctxt)
|
||||
if host:
|
||||
services = [s for s in services if s['host'] == host]
|
||||
if service:
|
||||
services = [s for s in services if s['binary'] == service]
|
||||
for svc in services:
|
||||
delta = now - (svc['updated_at'] or svc['created_at'])
|
||||
alive = (delta.seconds <= 15)
|
||||
art = (alive and ":-)") or "XXX"
|
||||
active = 'enabled'
|
||||
if svc['disabled']:
|
||||
active = 'disabled'
|
||||
print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'],
|
||||
active, art,
|
||||
svc['updated_at'])
|
||||
|
||||
def enable(self, host, service):
|
||||
"""Enable scheduling for a service
|
||||
args: host service"""
|
||||
ctxt = context.get_admin_context()
|
||||
svc = db.service_get_by_args(ctxt, host, service)
|
||||
if not svc:
|
||||
print "Unable to find service"
|
||||
return
|
||||
db.service_update(ctxt, svc['id'], {'disabled': False})
|
||||
|
||||
def disable(self, host, service):
|
||||
"""Disable scheduling for a service
|
||||
args: host service"""
|
||||
ctxt = context.get_admin_context()
|
||||
svc = db.service_get_by_args(ctxt, host, service)
|
||||
if not svc:
|
||||
print "Unable to find service"
|
||||
return
|
||||
db.service_update(ctxt, svc['id'], {'disabled': True})
|
||||
|
||||
|
||||
CATEGORIES = [
|
||||
('user', UserCommands),
|
||||
('project', ProjectCommands),
|
||||
@@ -459,7 +506,8 @@ CATEGORIES = [
|
||||
('shell', ShellCommands),
|
||||
('vpn', VpnCommands),
|
||||
('floating', FloatingIpCommands),
|
||||
('network', NetworkCommands)]
|
||||
('network', NetworkCommands),
|
||||
('service', ServiceCommands)]
|
||||
|
||||
|
||||
def lazy_match(name, key_value_tuples):
|
||||
|
||||
@@ -16,13 +16,13 @@ Here's a script you can use to install (and then run) Nova on Ubuntu or Debian (
|
||||
Step 2: Install dependencies
|
||||
----------------------------
|
||||
|
||||
Nova requires rabbitmq for messaging and optionally you can use redis for storing state, so install these first.
|
||||
Nova requires rabbitmq for messaging, so install that first.
|
||||
|
||||
*Note:* You must have sudo installed to run these commands as shown here.
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install rabbitmq-server redis-server
|
||||
sudo apt-get install rabbitmq-server
|
||||
|
||||
|
||||
You'll see messages starting with "Reading package lists... Done" and you must confirm by typing Y that you want to continue.
|
||||
@@ -31,11 +31,10 @@ If you're running on Ubuntu 10.04, you'll need to install Twisted and python-gfl
|
||||
|
||||
::
|
||||
|
||||
sudo apt-get install python-twisted
|
||||
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 95C71FE2
|
||||
sudo sh -c 'echo "deb http://ppa.launchpad.net/openstack/openstack-ppa/ubuntu lucid main" > /etc/apt/sources.list.d/openstackppa.list'
|
||||
sudo apt-get update && sudo apt-get install python-gflags
|
||||
sudo add-get install python-software-properties
|
||||
sudo add-apt-repository ppa:nova-core/trunk
|
||||
sudo apt-get update
|
||||
sudo apt-get install python-twisted python-gflags
|
||||
|
||||
|
||||
Once you've done this, continue at Step 3 here: :doc:`../single.node.install`
|
||||
|
||||
@@ -76,11 +76,11 @@ External unix tools that are required:
|
||||
* aoetools and vblade-persist (if you use aoe-volumes)
|
||||
|
||||
Nova uses cutting-edge versions of many packages. There are ubuntu packages in
|
||||
the nova-core ppa. You can use add this ppa to your sources list on an ubuntu
|
||||
machine with the following commands::
|
||||
the nova-core trunk ppa. You can use add this ppa to your sources list on an
|
||||
ubuntu machine with the following commands::
|
||||
|
||||
sudo apt-get install -y python-software-properties
|
||||
sudo add-apt-repository ppa:nova-core/ppa
|
||||
sudo add-apt-repository ppa:nova-core/trunk
|
||||
|
||||
Recommended
|
||||
-----------
|
||||
|
||||
@@ -45,29 +45,33 @@ Assumptions
|
||||
|
||||
|
||||
Step 1 - Use apt-get to get the latest code
|
||||
-----------------------------------------
|
||||
-------------------------------------------
|
||||
|
||||
1. Setup Nova PPA with https://launchpad.net/~nova-core/+archive/trunk. The ‘python-software-properties’ package is a pre-requisite for setting up the nova package repo:
|
||||
|
||||
::
|
||||
|
||||
apt-get -y install python-software-properties
|
||||
add-apt-repository ppa:nova-core/trunk
|
||||
sudo apt-get install python-software-properties
|
||||
sudo add-apt-repository ppa:nova-core/trunk
|
||||
|
||||
2. Update apt-get:
|
||||
2. Run update.
|
||||
|
||||
::
|
||||
|
||||
apt-get update
|
||||
sudo apt-get update
|
||||
|
||||
3. Install nova-packages (dependencies should be automatically installed).
|
||||
3. Install nova-pkgs (dependencies should be automatically installed).
|
||||
|
||||
::
|
||||
|
||||
apt-get -y install bzr nova-common nova-doc python-mysqldb python-greenlet python-nova nova-api nova-network nova-objectstore nova-scheduler nova-compute unzip vim euca2ools rabbitmq-server dnsmasq open-iscsi kpartx kvm gawk iptables ebtables user-mode-linux kvm libvirt-bin screen iscsitarget euca2ools vlan curl python-twisted python-sqlalchemy python-mox python-greenlet python-carrot python-daemon python-eventlet python-gflags python-libvirt python-libxml2 python-routes
|
||||
sudo apt-get install python-greenlet
|
||||
sudo apt-get install nova-common nova-doc python-nova nova-api nova-network nova-objectstore nova-scheduler
|
||||
|
||||
It is highly likely that there will be errors when the nova services come up since they are not yet configured. Don't worry, you're only at step 1!
|
||||
|
||||
Step 2 Setup configuration file (installed in /etc/nova)
|
||||
--------------------------------------------------------
|
||||
|
||||
Step 2 – Setting up nova.conf (installed in /etc/nova)
|
||||
---------------------------------------------------------
|
||||
1. Nova development has consolidated all config files to nova.conf as of November 2010. There is a default set of options that are already configured in nova.conf:
|
||||
|
||||
::
|
||||
@@ -133,7 +137,7 @@ chown -R root:nova /etc/nova
|
||||
chmod 644 /etc/nova/nova.conf
|
||||
|
||||
Step 3 - Setup the SQL DB (MySQL for this setup)
|
||||
-----------------------
|
||||
------------------------------------------------
|
||||
|
||||
1. First you 'preseed' to bypass all the installation prompts
|
||||
::
|
||||
|
||||
@@ -24,7 +24,7 @@ Routing
|
||||
|
||||
To map URLs to controllers+actions, OpenStack uses the Routes package, a clone of Rails routes for Python implementations. See http://routes.groovie.org/ fore more information.
|
||||
|
||||
URLs are mapped to "action" methods on "controller" classes in nova/api/openstack/__init__/ApiRouter.__init__ .
|
||||
URLs are mapped to "action" methods on "controller" classes in `nova/api/openstack/__init__/ApiRouter.__init__` .
|
||||
|
||||
See http://routes.groovie.org/manual.html for all syntax, but you'll probably just need these two:
|
||||
- mapper.connect() lets you map a single URL to a single action on a controller.
|
||||
@@ -33,9 +33,9 @@ See http://routes.groovie.org/manual.html for all syntax, but you'll probably ju
|
||||
Controllers and actions
|
||||
-----------------------
|
||||
|
||||
Controllers live in nova/api/openstack, and inherit from nova.wsgi.Controller.
|
||||
Controllers live in `nova/api/openstack`, and inherit from nova.wsgi.Controller.
|
||||
|
||||
See nova/api/openstack/servers.py for an example.
|
||||
See `nova/api/openstack/servers.py` for an example.
|
||||
|
||||
Action methods take parameters that are sucked out of the URL by mapper.connect() or .resource(). The first two parameters are self and the WebOb request, from which you can get the req.environ, req.body, req.headers, etc.
|
||||
|
||||
@@ -46,7 +46,7 @@ Actions return a dictionary, and wsgi.Controller serializes that to JSON or XML
|
||||
|
||||
If you define a new controller, you'll need to define a _serialization_metadata attribute on the class, to tell wsgi.Controller how to convert your dictionary to XML. It needs to know the singular form of any list tag (e.g. <servers> list contains <server> tags) and which dictionary keys are to be XML attributes as opposed to subtags (e.g. <server id="4"/> instead of <server><id>4</id></server>).
|
||||
|
||||
See nova/api/openstack/servers.py for an example.
|
||||
See `nova/api/openstack/servers.py` for an example.
|
||||
|
||||
Faults
|
||||
------
|
||||
|
||||
@@ -72,8 +72,8 @@ RPC Casts
|
||||
|
||||
The diagram below the message flow during an rp.cast operation:
|
||||
|
||||
1. a Topic Publisher is instantiated to send the message request to the queuing system.
|
||||
2. once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as 'topic') and passed to the Worker in charge of the task.
|
||||
1. A Topic Publisher is instantiated to send the message request to the queuing system.
|
||||
2. Once the message is dispatched by the exchange, it is fetched by the Topic Consumer dictated by the routing key (such as 'topic') and passed to the Worker in charge of the task.
|
||||
|
||||
.. image:: /images/rabbit/flow2.png
|
||||
:width: 60%
|
||||
|
||||
@@ -129,12 +129,12 @@ The simplest networking mode. Each instance receives a fixed ip from the pool.
|
||||
Flat DHCP Mode
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This is similar to the flat mode, in that all instances are attached to the same bridge. In this mode nova does a bit more configuration, it will attempt to bridge into an ethernet device (eth0 by default). It will also run dnsmasq as a dhcpserver listening on this bridge. Instances receive their fixed IPs by doing a dhcpdiscover.
|
||||
This is similar to the flat mode, in that all instances are attached to the same bridge. In this mode Nova does a bit more configuration, it will attempt to bridge into an ethernet device (eth0 by default). It will also run dnsmasq as a dhcpserver listening on this bridge. Instances receive their fixed IPs by doing a dhcpdiscover.
|
||||
|
||||
VLAN DHCP Mode
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This is the default networking mode and supports the most features. For multiple machine installation, it requires a switch that supports host-managed vlan tagging. In this mode, nova will create a vlan and bridge for each project. The project gets a range of private ips that are only accessible from inside the vlan. In order for a user to access the instances in their project, a special vpn instance (code named :ref:`cloudpipe <cloudpipe>`) needs to be created. Nova generates a certificate and key for the user to access the vpn and starts the vpn automatically. More information on cloudpipe can be found :ref:`here <cloudpipe>`.
|
||||
This is the default networking mode and supports the most features. For multiple machine installation, it requires a switch that supports host-managed vlan tagging. In this mode, Nova will create a vlan and bridge for each project. The project gets a range of private ips that are only accessible from inside the vlan. In order for a user to access the instances in their project, a special vpn instance (code named :ref:`cloudpipe <cloudpipe>`) needs to be created. Nova generates a certificate and key for the user to access the vpn and starts the vpn automatically. More information on cloudpipe can be found :ref:`here <cloudpipe>`.
|
||||
|
||||
The following diagram illustrates how the communication that occurs between the vlan (the dashed box) and the public internet (represented by the two clouds)
|
||||
|
||||
@@ -154,18 +154,16 @@ Concept: nova-manage
|
||||
--------------------
|
||||
|
||||
The nova-manage command is used to perform many essential functions for
|
||||
administration and ongoing maintenance of nova, such as user creation,
|
||||
administration and ongoing maintenance of Nova, such as user creation,
|
||||
vpn management, and much more.
|
||||
|
||||
See :ref:`nova.manage` in the Administration Guide for more details.
|
||||
|
||||
See :doc:`nova.manage` in the Administration Guide for more details.
|
||||
|
||||
Concept: Flags
|
||||
--------------
|
||||
|
||||
Nova uses python-gflags for a distributed command line system, and the flags can either be set when running a command at the command line or within a flag file. When you install Nova packages for the Austin release, each nova service gets its own flag file. For example, nova-network.conf is used for configuring the nova-network service, and so forth. In releases beyond Austin which was released in October 2010, all flags are set in nova.conf.
|
||||
|
||||
|
||||
Concept: Plugins
|
||||
----------------
|
||||
|
||||
@@ -181,7 +179,7 @@ Concept: Plugins
|
||||
Concept: IPC/RPC
|
||||
----------------
|
||||
|
||||
Nova utilizes the RabbitMQ implementation of the AMQP messaging standard for performing communication between the various nova services. This message queuing service is used for both local and remote communication because Nova is designed so that there is no requirement that any of the services exist on the same physical machine. RabbitMQ in particular is very robust and provides the efficiency and reliability that Nova needs. More information about RabbitMQ can be found at http://www.rabbitmq.com/.
|
||||
Nova utilizes the RabbitMQ implementation of the AMQP messaging standard for performing communication between the various Nova services. This message queuing service is used for both local and remote communication because Nova is designed so that there is no requirement that any of the services exist on the same physical machine. RabbitMQ in particular is very robust and provides the efficiency and reliability that Nova needs. More information about RabbitMQ can be found at http://www.rabbitmq.com/.
|
||||
|
||||
Concept: Fakes
|
||||
--------------
|
||||
|
||||
@@ -59,38 +59,21 @@ different configurations (though for more complex setups you should see
|
||||
* HOST_IP
|
||||
* Default: address of first interface from the ifconfig command
|
||||
* Values: 127.0.0.1, or any other valid address
|
||||
|
||||
TEST
|
||||
~~~~
|
||||
|
||||
**Default**: 0
|
||||
**Values**: 1, run tests after checkout and initial setup
|
||||
|
||||
USE_MYSQL
|
||||
~~~~~~~~~
|
||||
|
||||
**Default**: 0, use sqlite3
|
||||
**Values**: 1, use mysql instead of sqlite3
|
||||
|
||||
MYSQL_PASS
|
||||
~~~~~~~~~~
|
||||
|
||||
Only useful if $USE_MYSQL=1.
|
||||
|
||||
**Default**: nova
|
||||
**Values**: value of root password for mysql
|
||||
|
||||
USE_LDAP
|
||||
~~~~~~~~
|
||||
|
||||
**Default**: 0, use :mod:`nova.auth.dbdriver`
|
||||
**Values**: 1, use :mod:`nova.auth.ldapdriver`
|
||||
|
||||
LIBVIRT_TYPE
|
||||
~~~~~~~~~~~~
|
||||
|
||||
**Default**: qemu
|
||||
**Values**: uml, kvm
|
||||
* TEST
|
||||
* Default: 0
|
||||
* Values: 1, run tests after checkout and initial setup
|
||||
* USE_MYSQL
|
||||
* Default: 0, use sqlite3
|
||||
* Values: 1, use mysql instead of sqlite3
|
||||
* MYSQL_PASS (Only useful if $USE_MYSQL=1)
|
||||
* Default: nova
|
||||
* Values: value of root password for mysql
|
||||
* USE_LDAP
|
||||
* Default: 0, use :mod:`nova.auth.dbdriver`
|
||||
* Values: 1, use :mod:`nova.auth.ldapdriver`
|
||||
* LIBVIRT_TYPE
|
||||
* Default: qemu
|
||||
* Values: uml, kvm
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
@@ -212,6 +212,8 @@ DEFINE_list('region_list',
|
||||
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
|
||||
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
|
||||
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
|
||||
DEFINE_integer('glance_port', 9292, 'glance port')
|
||||
DEFINE_string('glance_host', utils.get_my_ip(), 'glance host')
|
||||
DEFINE_integer('s3_port', 3333, 's3 port')
|
||||
DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
|
||||
DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
|
||||
@@ -239,6 +241,7 @@ DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
|
||||
DEFINE_integer('cc_port', 8773, 'cloud controller port')
|
||||
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
|
||||
|
||||
DEFINE_string('default_project', 'openstack', 'default project for openstack')
|
||||
DEFINE_string('default_image', 'ami-11111',
|
||||
'default image to use, testing only')
|
||||
DEFINE_string('default_instance_type', 'm1.small',
|
||||
@@ -260,6 +263,9 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
|
||||
DEFINE_string('sql_connection',
|
||||
'sqlite:///$state_path/nova.sqlite',
|
||||
'connection string for sql database')
|
||||
DEFINE_string('sql_idle_timeout',
|
||||
'3600',
|
||||
'timeout for idle sql database connections')
|
||||
|
||||
DEFINE_string('compute_manager', 'nova.compute.manager.ComputeManager',
|
||||
'Manager for compute')
|
||||
|
||||
@@ -37,6 +37,11 @@ class NoValidHost(exception.Error):
|
||||
pass
|
||||
|
||||
|
||||
class WillNotSchedule(exception.Error):
|
||||
"""The specified host is not up or doesn't exist."""
|
||||
pass
|
||||
|
||||
|
||||
class Scheduler(object):
|
||||
"""The base class that all Scheduler clases should inherit from."""
|
||||
|
||||
|
||||
@@ -43,6 +43,19 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
def schedule_run_instance(self, context, instance_id, *_args, **_kwargs):
|
||||
"""Picks a host that is up and has the fewest running instances."""
|
||||
instance_ref = db.instance_get(context, instance_id)
|
||||
if instance_ref['availability_zone'] and context.is_admin:
|
||||
zone, _x, host = instance_ref['availability_zone'].partition(':')
|
||||
service = db.service_get_by_args(context.elevated(), host,
|
||||
'nova-compute')
|
||||
if not self.service_is_up(service):
|
||||
raise driver.WillNotSchedule("Host %s is not alive" % host)
|
||||
|
||||
# TODO(vish): this probably belongs in the manager, if we
|
||||
# can generalize this somehow
|
||||
now = datetime.datetime.utcnow()
|
||||
db.instance_update(context, instance_id, {'host': host,
|
||||
'scheduled_at': now})
|
||||
return host
|
||||
results = db.service_get_all_compute_sorted(context)
|
||||
for result in results:
|
||||
(service, instance_cores) = result
|
||||
@@ -62,6 +75,19 @@ class SimpleScheduler(chance.ChanceScheduler):
|
||||
def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
|
||||
"""Picks a host that is up and has the fewest volumes."""
|
||||
volume_ref = db.volume_get(context, volume_id)
|
||||
if (':' in volume_ref['availability_zone']) and context.is_admin:
|
||||
zone, _x, host = volume_ref['availability_zone'].partition(':')
|
||||
service = db.service_get_by_args(context.elevated(), host,
|
||||
'nova-volume')
|
||||
if not self.service_is_up(service):
|
||||
raise driver.WillNotSchedule("Host %s not available" % host)
|
||||
|
||||
# TODO(vish): this probably belongs in the manager, if we
|
||||
# can generalize this somehow
|
||||
now = datetime.datetime.utcnow()
|
||||
db.volume_update(context, volume_id, {'host': host,
|
||||
'scheduled_at': now})
|
||||
return host
|
||||
results = db.service_get_all_volume_sorted(context)
|
||||
for result in results:
|
||||
(service, volume_gigabytes) = result
|
||||
|
||||
@@ -101,13 +101,13 @@ class ComputeTestCase(test.TestCase):
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
|
||||
instances = db.instance_get_all(context.get_admin_context())
|
||||
logging.info("Running instances: %s", instances)
|
||||
logging.info(_("Running instances: %s"), instances)
|
||||
self.assertEqual(len(instances), 1)
|
||||
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
instances = db.instance_get_all(context.get_admin_context())
|
||||
logging.info("After terminating instances: %s", instances)
|
||||
logging.info(_("After terminating instances: %s"), instances)
|
||||
self.assertEqual(len(instances), 0)
|
||||
|
||||
def test_run_terminate_timestamps(self):
|
||||
@@ -136,6 +136,14 @@ class ComputeTestCase(test.TestCase):
|
||||
self.compute.unpause_instance(self.context, instance_id)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_suspend(self):
|
||||
"""ensure instance can be suspended"""
|
||||
instance_id = self._create_instance()
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.compute.suspend_instance(self.context, instance_id)
|
||||
self.compute.resume_instance(self.context, instance_id)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_reboot(self):
|
||||
"""Ensure instance can be rebooted"""
|
||||
instance_id = self._create_instance()
|
||||
@@ -143,6 +151,14 @@ class ComputeTestCase(test.TestCase):
|
||||
self.compute.reboot_instance(self.context, instance_id)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_snapshot(self):
|
||||
"""Ensure instance can be snapshotted"""
|
||||
instance_id = self._create_instance()
|
||||
name = "myfakesnapshot"
|
||||
self.compute.run_instance(self.context, instance_id)
|
||||
self.compute.snapshot_instance(self.context, instance_id, name)
|
||||
self.compute.terminate_instance(self.context, instance_id)
|
||||
|
||||
def test_console_output(self):
|
||||
"""Make sure we can get console output from instance"""
|
||||
instance_id = self._create_instance()
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
Tests For Scheduler
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
@@ -33,6 +35,7 @@ from nova.scheduler import driver
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('max_cores', 'nova.scheduler.simple')
|
||||
flags.DECLARE('stub_network', 'nova.compute.manager')
|
||||
|
||||
|
||||
class TestDriver(driver.Scheduler):
|
||||
@@ -94,7 +97,7 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
self.manager.delete_user(self.user)
|
||||
self.manager.delete_project(self.project)
|
||||
|
||||
def _create_instance(self):
|
||||
def _create_instance(self, **kwargs):
|
||||
"""Create a test instance"""
|
||||
inst = {}
|
||||
inst['image_id'] = 'ami-test'
|
||||
@@ -105,6 +108,7 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
inst['mac_address'] = utils.generate_mac()
|
||||
inst['ami_launch_index'] = 0
|
||||
inst['vcpus'] = 1
|
||||
inst['availability_zone'] = kwargs.get('availability_zone', None)
|
||||
return db.instance_create(self.context, inst)['id']
|
||||
|
||||
def _create_volume(self):
|
||||
@@ -113,9 +117,33 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
vol['image_id'] = 'ami-test'
|
||||
vol['reservation_id'] = 'r-fakeres'
|
||||
vol['size'] = 1
|
||||
vol['availability_zone'] = 'test'
|
||||
return db.volume_create(self.context, vol)['id']
|
||||
|
||||
def test_hosts_are_up(self):
|
||||
def test_doesnt_report_disabled_hosts_as_up(self):
|
||||
"""Ensures driver doesn't find hosts before they are enabled"""
|
||||
# NOTE(vish): constructing service without create method
|
||||
# because we are going to use it without queue
|
||||
compute1 = service.Service('host1',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.start()
|
||||
compute2 = service.Service('host2',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute2.start()
|
||||
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
|
||||
s2 = db.service_get_by_args(self.context, 'host2', 'nova-compute')
|
||||
db.service_update(self.context, s1['id'], {'disabled': True})
|
||||
db.service_update(self.context, s2['id'], {'disabled': True})
|
||||
hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
|
||||
self.assertEqual(0, len(hosts))
|
||||
compute1.kill()
|
||||
compute2.kill()
|
||||
|
||||
def test_reports_enabled_hosts_as_up(self):
|
||||
"""Ensures driver can find the hosts that are up"""
|
||||
# NOTE(vish): constructing service without create method
|
||||
# because we are going to use it without queue
|
||||
@@ -130,7 +158,7 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
FLAGS.compute_manager)
|
||||
compute2.start()
|
||||
hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
|
||||
self.assertEqual(len(hosts), 2)
|
||||
self.assertEqual(2, len(hosts))
|
||||
compute1.kill()
|
||||
compute2.kill()
|
||||
|
||||
@@ -157,6 +185,63 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
compute1.kill()
|
||||
compute2.kill()
|
||||
|
||||
def test_specific_host_gets_instance(self):
|
||||
"""Ensures if you set availability_zone it launches on that zone"""
|
||||
compute1 = service.Service('host1',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.start()
|
||||
compute2 = service.Service('host2',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute2.start()
|
||||
instance_id1 = self._create_instance()
|
||||
compute1.run_instance(self.context, instance_id1)
|
||||
instance_id2 = self._create_instance(availability_zone='nova:host1')
|
||||
host = self.scheduler.driver.schedule_run_instance(self.context,
|
||||
instance_id2)
|
||||
self.assertEqual('host1', host)
|
||||
compute1.terminate_instance(self.context, instance_id1)
|
||||
db.instance_destroy(self.context, instance_id2)
|
||||
compute1.kill()
|
||||
compute2.kill()
|
||||
|
||||
def test_wont_sechedule_if_specified_host_is_down(self):
|
||||
compute1 = service.Service('host1',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.start()
|
||||
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
|
||||
now = datetime.datetime.utcnow()
|
||||
delta = datetime.timedelta(seconds=FLAGS.service_down_time * 2)
|
||||
past = now - delta
|
||||
db.service_update(self.context, s1['id'], {'updated_at': past})
|
||||
instance_id2 = self._create_instance(availability_zone='nova:host1')
|
||||
self.assertRaises(driver.WillNotSchedule,
|
||||
self.scheduler.driver.schedule_run_instance,
|
||||
self.context,
|
||||
instance_id2)
|
||||
db.instance_destroy(self.context, instance_id2)
|
||||
compute1.kill()
|
||||
|
||||
def test_will_schedule_on_disabled_host_if_specified(self):
|
||||
compute1 = service.Service('host1',
|
||||
'nova-compute',
|
||||
'compute',
|
||||
FLAGS.compute_manager)
|
||||
compute1.start()
|
||||
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
|
||||
db.service_update(self.context, s1['id'], {'disabled': True})
|
||||
instance_id2 = self._create_instance(availability_zone='nova:host1')
|
||||
host = self.scheduler.driver.schedule_run_instance(self.context,
|
||||
instance_id2)
|
||||
self.assertEqual('host1', host)
|
||||
db.instance_destroy(self.context, instance_id2)
|
||||
compute1.kill()
|
||||
|
||||
def test_too_many_cores(self):
|
||||
"""Ensures we don't go over max cores"""
|
||||
compute1 = service.Service('host1',
|
||||
|
||||
@@ -33,6 +33,7 @@ flags.DECLARE('instances_path', 'nova.compute.manager')
|
||||
class LibvirtConnTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(LibvirtConnTestCase, self).setUp()
|
||||
libvirt_conn._late_load_cheetah()
|
||||
self.flags(fake_call=True)
|
||||
self.manager = manager.AuthManager()
|
||||
self.user = self.manager.create_user('fake', 'fake', 'fake',
|
||||
@@ -157,7 +158,6 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
(lambda t: t.find('./devices/serial/source').get(
|
||||
'path').split('/')[1], 'console.log'),
|
||||
(lambda t: t.find('./memory').text, '2097152')]
|
||||
|
||||
if rescue:
|
||||
common_checks += [
|
||||
(lambda t: t.findall('./devices/disk/source')[0].get(
|
||||
|
||||
@@ -29,9 +29,9 @@ from nova.auth import manager
|
||||
from nova.compute import instance_types
|
||||
from nova.compute import power_state
|
||||
from nova.virt import xenapi_conn
|
||||
from nova.virt.xenapi import fake
|
||||
from nova.virt.xenapi import fake as xenapi_fake
|
||||
from nova.virt.xenapi import volume_utils
|
||||
from nova.tests.db import fakes
|
||||
from nova.tests.db import fakes as db_fakes
|
||||
from nova.tests.xenapi import stubs
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
@@ -47,8 +47,9 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
FLAGS.target_host = '127.0.0.1'
|
||||
FLAGS.xenapi_connection_url = 'test_url'
|
||||
FLAGS.xenapi_connection_password = 'test_pass'
|
||||
fakes.stub_out_db_instance_api(self.stubs)
|
||||
fake.reset()
|
||||
db_fakes.stub_out_db_instance_api(self.stubs)
|
||||
stubs.stub_out_get_target(self.stubs)
|
||||
xenapi_fake.reset()
|
||||
self.values = {'name': 1, 'id': 1,
|
||||
'project_id': 'fake',
|
||||
'user_id': 'fake',
|
||||
@@ -82,7 +83,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
label = 'SR-%s' % vol['ec2_id']
|
||||
description = 'Test-SR'
|
||||
sr_ref = helper.create_iscsi_storage(session, info, label, description)
|
||||
srs = fake.get_all('SR')
|
||||
srs = xenapi_fake.get_all('SR')
|
||||
self.assertEqual(sr_ref, srs[0])
|
||||
db.volume_destroy(context.get_admin_context(), vol['id'])
|
||||
|
||||
@@ -106,17 +107,17 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
volume = self._create_volume()
|
||||
instance = db.instance_create(self.values)
|
||||
fake.create_vm(instance.name, 'Running')
|
||||
xenapi_fake.create_vm(instance.name, 'Running')
|
||||
result = conn.attach_volume(instance.name, volume['ec2_id'],
|
||||
'/dev/sdc')
|
||||
|
||||
def check():
|
||||
# check that the VM has a VBD attached to it
|
||||
# Get XenAPI reference for the VM
|
||||
vms = fake.get_all('VM')
|
||||
vms = xenapi_fake.get_all('VM')
|
||||
# Get XenAPI record for VBD
|
||||
vbds = fake.get_all('VBD')
|
||||
vbd = fake.get_record('VBD', vbds[0])
|
||||
vbds = xenapi_fake.get_all('VBD')
|
||||
vbd = xenapi_fake.get_record('VBD', vbds[0])
|
||||
vm_ref = vbd['VM']
|
||||
self.assertEqual(vm_ref, vms[0])
|
||||
|
||||
@@ -129,7 +130,7 @@ class XenAPIVolumeTestCase(test.TestCase):
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
volume = self._create_volume()
|
||||
instance = db.instance_create(self.values)
|
||||
fake.create_vm(instance.name, 'Running')
|
||||
xenapi_fake.create_vm(instance.name, 'Running')
|
||||
self.assertRaises(Exception,
|
||||
conn.attach_volume,
|
||||
instance.name,
|
||||
@@ -155,41 +156,70 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.stubs = stubout.StubOutForTesting()
|
||||
FLAGS.xenapi_connection_url = 'test_url'
|
||||
FLAGS.xenapi_connection_password = 'test_pass'
|
||||
fake.reset()
|
||||
fakes.stub_out_db_instance_api(self.stubs)
|
||||
fake.create_network('fake', FLAGS.flat_network_bridge)
|
||||
xenapi_fake.reset()
|
||||
db_fakes.stub_out_db_instance_api(self.stubs)
|
||||
xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||
self.conn = xenapi_conn.get_connection(False)
|
||||
|
||||
def test_list_instances_0(self):
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
instances = conn.list_instances()
|
||||
instances = self.conn.list_instances()
|
||||
self.assertEquals(instances, [])
|
||||
|
||||
def test_spawn(self):
|
||||
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
|
||||
values = {'name': 1, 'id': 1,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'image_id': 1,
|
||||
'kernel_id': 2,
|
||||
'ramdisk_id': 3,
|
||||
'instance_type': 'm1.large',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff',
|
||||
}
|
||||
conn = xenapi_conn.get_connection(False)
|
||||
instance = db.instance_create(values)
|
||||
conn.spawn(instance)
|
||||
def test_get_diagnostics(self):
|
||||
instance = self._create_instance()
|
||||
self.conn.get_diagnostics(instance)
|
||||
|
||||
def test_instance_snapshot(self):
|
||||
stubs.stubout_instance_snapshot(self.stubs)
|
||||
instance = self._create_instance()
|
||||
|
||||
name = "MySnapshot"
|
||||
template_vm_ref = self.conn.snapshot(instance, name)
|
||||
|
||||
def ensure_vm_was_torn_down():
|
||||
vm_labels = []
|
||||
for vm_ref in xenapi_fake.get_all('VM'):
|
||||
vm_rec = xenapi_fake.get_record('VM', vm_ref)
|
||||
if not vm_rec["is_control_domain"]:
|
||||
vm_labels.append(vm_rec["name_label"])
|
||||
|
||||
self.assertEquals(vm_labels, [1])
|
||||
|
||||
def ensure_vbd_was_torn_down():
|
||||
vbd_labels = []
|
||||
for vbd_ref in xenapi_fake.get_all('VBD'):
|
||||
vbd_rec = xenapi_fake.get_record('VBD', vbd_ref)
|
||||
vbd_labels.append(vbd_rec["vm_name_label"])
|
||||
|
||||
self.assertEquals(vbd_labels, [1])
|
||||
|
||||
def ensure_vdi_was_torn_down():
|
||||
for vdi_ref in xenapi_fake.get_all('VDI'):
|
||||
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
|
||||
name_label = vdi_rec["name_label"]
|
||||
self.assert_(not name_label.endswith('snapshot'))
|
||||
|
||||
def check():
|
||||
instances = conn.list_instances()
|
||||
ensure_vm_was_torn_down()
|
||||
ensure_vbd_was_torn_down()
|
||||
ensure_vdi_was_torn_down()
|
||||
|
||||
check()
|
||||
|
||||
def test_spawn(self):
|
||||
instance = self._create_instance()
|
||||
|
||||
def check():
|
||||
instances = self.conn.list_instances()
|
||||
self.assertEquals(instances, [1])
|
||||
|
||||
# Get Nova record for VM
|
||||
vm_info = conn.get_info(1)
|
||||
vm_info = self.conn.get_info(1)
|
||||
|
||||
# Get XenAPI record for VM
|
||||
vms = fake.get_all('VM')
|
||||
vm = fake.get_record('VM', vms[0])
|
||||
vms = xenapi_fake.get_all('VM')
|
||||
vm = xenapi_fake.get_record('VM', vms[0])
|
||||
|
||||
# Check that m1.large above turned into the right thing.
|
||||
instance_type = instance_types.INSTANCE_TYPES['m1.large']
|
||||
@@ -217,3 +247,18 @@ class XenAPIVMTestCase(test.TestCase):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
self.stubs.UnsetAll()
|
||||
|
||||
def _create_instance(self):
|
||||
"""Creates and spawns a test instance"""
|
||||
values = {'name': 1, 'id': 1,
|
||||
'project_id': self.project.id,
|
||||
'user_id': self.user.id,
|
||||
'image_id': 1,
|
||||
'kernel_id': 2,
|
||||
'ramdisk_id': 3,
|
||||
'instance_type': 'm1.large',
|
||||
'mac_address': 'aa:bb:cc:dd:ee:ff'
|
||||
}
|
||||
instance = db.instance_create(values)
|
||||
self.conn.spawn(instance)
|
||||
return instance
|
||||
|
||||
@@ -18,6 +18,55 @@
|
||||
|
||||
from nova.virt import xenapi_conn
|
||||
from nova.virt.xenapi import fake
|
||||
from nova.virt.xenapi import volume_utils
|
||||
from nova.virt.xenapi import vm_utils
|
||||
|
||||
|
||||
def stubout_instance_snapshot(stubs):
|
||||
@classmethod
|
||||
def fake_fetch_image(cls, session, instance_id, image, user, project,
|
||||
type):
|
||||
# Stubout wait_for_task
|
||||
def fake_wait_for_task(self, id, task):
|
||||
class FakeEvent:
|
||||
|
||||
def send(self, value):
|
||||
self.rv = value
|
||||
|
||||
def wait(self):
|
||||
return self.rv
|
||||
|
||||
done = FakeEvent()
|
||||
self._poll_task(id, task, done)
|
||||
rv = done.wait()
|
||||
return rv
|
||||
|
||||
stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task',
|
||||
fake_wait_for_task)
|
||||
|
||||
from nova.virt.xenapi.fake import create_vdi
|
||||
name_label = "instance-%s" % instance_id
|
||||
#TODO: create fake SR record
|
||||
sr_ref = "fakesr"
|
||||
vdi_ref = create_vdi(name_label=name_label, read_only=False,
|
||||
sr_ref=sr_ref, sharable=False)
|
||||
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
|
||||
vdi_uuid = vdi_rec['uuid']
|
||||
return vdi_uuid
|
||||
|
||||
stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
|
||||
|
||||
def fake_parse_xmlrpc_value(val):
|
||||
return val
|
||||
|
||||
stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value)
|
||||
|
||||
def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
|
||||
original_parent_uuid):
|
||||
#TODO(sirp): Should we actually fake out the data here
|
||||
return "fakeparent"
|
||||
|
||||
stubs.Set(vm_utils, 'wait_for_vhd_coalesce', fake_wait_for_vhd_coalesce)
|
||||
|
||||
|
||||
def stubout_session(stubs, cls):
|
||||
@@ -34,6 +83,14 @@ def stubout_session(stubs, cls):
|
||||
fake_import)
|
||||
|
||||
|
||||
def stub_out_get_target(stubs):
|
||||
"""Stubs out _get_target in volume_utils"""
|
||||
def fake_get_target(volume_id):
|
||||
return (None, None)
|
||||
|
||||
stubs.Set(volume_utils, '_get_target', fake_get_target)
|
||||
|
||||
|
||||
class FakeSessionForVMTests(fake.SessionBase):
|
||||
""" Stubs out a XenAPISession for VM tests """
|
||||
def __init__(self, uri):
|
||||
@@ -54,6 +111,24 @@ class FakeSessionForVMTests(fake.SessionBase):
|
||||
vm['is_a_template'] = False
|
||||
vm['is_control_domain'] = False
|
||||
|
||||
def VM_snapshot(self, session_ref, vm_ref, label):
|
||||
status = "Running"
|
||||
template_vm_ref = fake.create_vm(label, status, is_a_template=True,
|
||||
is_control_domain=False)
|
||||
|
||||
sr_ref = "fakesr"
|
||||
template_vdi_ref = fake.create_vdi(label, read_only=True,
|
||||
sr_ref=sr_ref, sharable=False)
|
||||
|
||||
template_vbd_ref = fake.create_vbd(template_vm_ref, template_vdi_ref)
|
||||
return template_vm_ref
|
||||
|
||||
def VDI_destroy(self, session_ref, vdi_ref):
|
||||
fake.destroy_vdi(vdi_ref)
|
||||
|
||||
def VM_destroy(self, session_ref, vm_ref):
|
||||
fake.destroy_vm(vm_ref)
|
||||
|
||||
|
||||
class FakeSessionForVolumeTests(fake.SessionBase):
|
||||
""" Stubs out a XenAPISession for Volume tests """
|
||||
|
||||
@@ -48,7 +48,8 @@ def import_class(import_str):
|
||||
try:
|
||||
__import__(mod_str)
|
||||
return getattr(sys.modules[mod_str], class_str)
|
||||
except (ImportError, ValueError, AttributeError):
|
||||
except (ImportError, ValueError, AttributeError), exc:
|
||||
logging.debug(_('Inner Exception: %s'), exc)
|
||||
raise exception.NotFound(_('Class %s cannot be found') % class_str)
|
||||
|
||||
|
||||
|
||||
@@ -270,7 +270,7 @@ class Serializer(object):
|
||||
needed to serialize a dictionary to that type.
|
||||
"""
|
||||
self.metadata = metadata or {}
|
||||
req = webob.Request(environ)
|
||||
req = webob.Request.blank('', environ)
|
||||
suffix = req.path_info.split('.')[-1].lower()
|
||||
if suffix == 'json':
|
||||
self.handler = self._to_json
|
||||
|
||||
68
run_tests.py
Normal file
68
run_tests.py
Normal file
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from nose import config
|
||||
from nose import result
|
||||
from nose import core
|
||||
|
||||
|
||||
class NovaTestResult(result.TextTestResult):
|
||||
def __init__(self, *args, **kw):
|
||||
result.TextTestResult.__init__(self, *args, **kw)
|
||||
self._last_case = None
|
||||
|
||||
def getDescription(self, test):
|
||||
return str(test)
|
||||
|
||||
def startTest(self, test):
|
||||
unittest.TestResult.startTest(self, test)
|
||||
current_case = test.test.__class__.__name__
|
||||
|
||||
if self.showAll:
|
||||
if current_case != self._last_case:
|
||||
self.stream.writeln(current_case)
|
||||
self._last_case = current_case
|
||||
|
||||
self.stream.write(
|
||||
' %s' % str(test.test._testMethodName).ljust(60))
|
||||
self.stream.flush()
|
||||
|
||||
|
||||
class NovaTestRunner(core.TextTestRunner):
|
||||
def _makeResult(self):
|
||||
return NovaTestResult(self.stream,
|
||||
self.descriptions,
|
||||
self.verbosity,
|
||||
self.config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
c = config.Config(stream=sys.stdout,
|
||||
env=os.environ,
|
||||
verbosity=3)
|
||||
|
||||
runner = NovaTestRunner(stream=c.stream,
|
||||
verbosity=c.verbosity,
|
||||
config=c)
|
||||
sys.exit(not core.run(config=c, testRunner=runner))
|
||||
13
run_tests.sh
13
run_tests.sh
@@ -21,6 +21,7 @@ function process_option {
|
||||
-V|--virtual-env) let always_venv=1; let never_venv=0;;
|
||||
-N|--no-virtual-env) let always_venv=0; let never_venv=1;;
|
||||
-f|--force) let force=1;;
|
||||
*) noseargs="$noseargs $1"
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -29,15 +30,19 @@ with_venv=tools/with_venv.sh
|
||||
always_venv=0
|
||||
never_venv=0
|
||||
force=0
|
||||
noseargs=
|
||||
|
||||
|
||||
for arg in "$@"; do
|
||||
process_option $arg
|
||||
done
|
||||
|
||||
NOSETESTS="python run_tests.py $noseargs"
|
||||
|
||||
if [ $never_venv -eq 1 ]; then
|
||||
# Just run the test suites in current environment
|
||||
rm -f nova.sqlite
|
||||
nosetests -v
|
||||
$NOSETESTS 2> run_tests.err.log
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -49,7 +54,7 @@ fi
|
||||
|
||||
if [ -e ${venv} ]; then
|
||||
${with_venv} rm -f nova.sqlite
|
||||
${with_venv} nosetests -v $@
|
||||
${with_venv} $NOSETESTS 2> run_tests.err.log
|
||||
else
|
||||
if [ $always_venv -eq 1 ]; then
|
||||
# Automatically install the virtualenv
|
||||
@@ -62,10 +67,10 @@ else
|
||||
python tools/install_venv.py
|
||||
else
|
||||
rm -f nova.sqlite
|
||||
nosetests -v
|
||||
$NOSETESTS 2> run_tests.err.log
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
${with_venv} rm -f nova.sqlite
|
||||
${with_venv} nosetests -v $@
|
||||
${with_venv} $NOSETESTS 2> run_tests.err.log
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user