summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--HACKING.rst12
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml2
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml2
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml2
-rw-r--r--doc/api_samples/os-fping/fping-get-details-resp.json7
-rw-r--r--doc/api_samples/os-fping/fping-get-details-resp.xml6
-rw-r--r--doc/api_samples/os-fping/fping-get-resp.json9
-rw-r--r--doc/api_samples/os-fping/fping-get-resp.xml8
-rw-r--r--doc/api_samples/os-fping/server-post-req.json16
-rw-r--r--doc/api_samples/os-fping/server-post-req.xml19
-rw-r--r--doc/api_samples/os-fping/server-post-resp.json16
-rw-r--r--doc/api_samples/os-fping/server-post-resp.xml6
-rw-r--r--etc/nova/rootwrap.d/compute.filters6
-rw-r--r--nova/api/ec2/cloud.py16
-rw-r--r--nova/api/openstack/__init__.py2
-rw-r--r--nova/api/openstack/compute/contrib/aggregates.py2
-rw-r--r--nova/api/openstack/compute/contrib/availability_zone.py2
-rw-r--r--nova/api/openstack/compute/contrib/flavorextraspecs.py2
-rw-r--r--nova/api/openstack/compute/contrib/floating_ips_bulk.py4
-rw-r--r--nova/api/openstack/compute/contrib/simple_tenant_usage.py2
-rw-r--r--nova/api/openstack/compute/image_metadata.py4
-rw-r--r--nova/api/openstack/compute/servers.py4
-rw-r--r--nova/api/openstack/wsgi.py2
-rw-r--r--nova/availability_zones.py2
-rw-r--r--nova/common/memorycache.py2
-rw-r--r--nova/compute/vm_mode.py2
-rw-r--r--nova/db/sqlalchemy/api.py2
-rw-r--r--nova/db/sqlalchemy/session.py24
-rw-r--r--nova/image/glance.py2
-rw-r--r--nova/scheduler/driver.py2
-rw-r--r--nova/scheduler/filters/trusted_filter.py2
-rw-r--r--nova/scheduler/manager.py2
-rw-r--r--nova/service.py2
-rw-r--r--nova/tests/api/ec2/test_cloud.py13
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py11
-rw-r--r--nova/tests/api/openstack/fakes.py8
-rw-r--r--nova/tests/compute/test_resource_tracker.py2
-rw-r--r--nova/tests/fakeguestfs.py14
-rw-r--r--nova/tests/integrated/api/client.py2
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.json.tpl7
-rw-r--r--nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.xml.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-fping/fping-get-resp.json.tpl9
-rw-r--r--nova/tests/integrated/api_samples/os-fping/fping-get-resp.xml.tpl8
-rw-r--r--nova/tests/integrated/api_samples/os-fping/server-post-req.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-fping/server-post-req.xml.tpl19
-rw-r--r--nova/tests/integrated/api_samples/os-fping/server-post-resp.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-fping/server-post-resp.xml.tpl6
-rw-r--r--nova/tests/integrated/integrated_helpers.py2
-rw-r--r--nova/tests/integrated/test_api_samples.py91
-rw-r--r--nova/tests/scheduler/test_chance_scheduler.py4
-rw-r--r--nova/tests/scheduler/test_filter_scheduler.py7
-rw-r--r--nova/tests/scheduler/test_scheduler.py48
-rw-r--r--nova/tests/test_db_api.py19
-rw-r--r--nova/tests/test_libvirt.py4
-rw-r--r--nova/tests/test_sqlalchemy.py63
-rw-r--r--nova/tests/test_virt_disk_vfs_localfs.py10
-rw-r--r--nova/tests/test_xenapi.py72
-rw-r--r--nova/tests/virt/xenapi/imageupload/__init__.py0
-rw-r--r--nova/tests/virt/xenapi/imageupload/test_glance.py74
-rw-r--r--nova/utils.py2
-rw-r--r--nova/virt/baremetal/driver.py2
-rw-r--r--nova/virt/baremetal/volume_driver.py2
-rw-r--r--nova/virt/fake.py4
-rw-r--r--nova/virt/libvirt/driver.py2
-rw-r--r--nova/virt/libvirt/firewall.py13
-rw-r--r--nova/virt/libvirt/imagecache.py6
-rw-r--r--nova/virt/libvirt/utils.py2
-rw-r--r--nova/virt/libvirt/volume.py74
-rw-r--r--nova/virt/libvirt/volume_nfs.py19
-rw-r--r--nova/virt/xenapi/fake.py14
-rw-r--r--nova/virt/xenapi/imageupload/__init__.py0
-rw-r--r--nova/virt/xenapi/imageupload/glance.py54
-rw-r--r--nova/virt/xenapi/vm_utils.py31
-rw-r--r--nova/virt/xenapi/vmops.py25
-rw-r--r--openstack-common.conf2
-rw-r--r--tools/install_venv.py209
-rw-r--r--tools/install_venv_common.py225
98 files changed, 1092 insertions, 408 deletions
diff --git a/HACKING.rst b/HACKING.rst
index 35493e55b..54e3b3275 100644
--- a/HACKING.rst
+++ b/HACKING.rst
@@ -29,6 +29,18 @@ General
mylist = Foo().list() # OKAY, does not shadow built-in
+- Use the "not in" operator for collection membership evaluation. Example::
+
+ if not X in Y: # BAD, hard to understand
+ pass
+
+ if X not in Y: # OKAY, intuitive
+ pass
+
+ if not (X in Y or X is Z): # OKAY, still better than all those 'not's
+ pass
+
+
Imports
-------
- Do not import objects, only modules (*)
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml
new file mode 100644
index 000000000..95c1daab9
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<extra_specs>
+ <key1>value1</key1>
+ <key2>value2</key2>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml
new file mode 100644
index 000000000..06b01a9fc
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>value2</key2>
+ <key1>value1</key1>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json
new file mode 100644
index 000000000..e71755fe6
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json
@@ -0,0 +1,3 @@
+{
+ "key1": "value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml
new file mode 100644
index 000000000..d57579ba6
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">value1</extra_spec> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml
new file mode 100644
index 000000000..06b01a9fc
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>value2</key2>
+ <key1>value1</key1>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json
new file mode 100644
index 000000000..a40d79e32
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json
@@ -0,0 +1,3 @@
+{
+ "key1": "new_value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml
new file mode 100644
index 000000000..b7ae6732b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <key1>new_value1</key1> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json
new file mode 100644
index 000000000..a40d79e32
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json
@@ -0,0 +1,3 @@
+{
+ "key1": "new_value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml
new file mode 100644
index 000000000..13208ad7c
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">new_value1</extra_spec> \ No newline at end of file
diff --git a/doc/api_samples/os-fping/fping-get-details-resp.json b/doc/api_samples/os-fping/fping-get-details-resp.json
new file mode 100644
index 000000000..a5692832b
--- /dev/null
+++ b/doc/api_samples/os-fping/fping-get-details-resp.json
@@ -0,0 +1,7 @@
+{
+ "server": {
+ "alive": false,
+ "id": "f5e6fd6d-c0a3-4f9e-aabf-d69196b6d11a",
+ "project_id": "openstack"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-fping/fping-get-details-resp.xml b/doc/api_samples/os-fping/fping-get-details-resp.xml
new file mode 100644
index 000000000..5b3cb4785
--- /dev/null
+++ b/doc/api_samples/os-fping/fping-get-details-resp.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<server>
+ <project_id>openstack</project_id>
+ <id>69d3caab-ed51-4ee7-9d4b-941ee1b45484</id>
+ <alive>False</alive>
+</server> \ No newline at end of file
diff --git a/doc/api_samples/os-fping/fping-get-resp.json b/doc/api_samples/os-fping/fping-get-resp.json
new file mode 100644
index 000000000..11bf37edd
--- /dev/null
+++ b/doc/api_samples/os-fping/fping-get-resp.json
@@ -0,0 +1,9 @@
+{
+ "servers": [
+ {
+ "alive": false,
+ "id": "1d1aea35-472b-40cf-9337-8eb68480aaa1",
+ "project_id": "openstack"
+ }
+ ]
+} \ No newline at end of file
diff --git a/doc/api_samples/os-fping/fping-get-resp.xml b/doc/api_samples/os-fping/fping-get-resp.xml
new file mode 100644
index 000000000..dbf03778b
--- /dev/null
+++ b/doc/api_samples/os-fping/fping-get-resp.xml
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<servers>
+ <server>
+ <project_id>openstack</project_id>
+ <id>6a576ebe-8777-473a-ab95-8df34a50dedd</id>
+ <alive>False</alive>
+ </server>
+</servers> \ No newline at end of file
diff --git a/doc/api_samples/os-fping/server-post-req.json b/doc/api_samples/os-fping/server-post-req.json
new file mode 100644
index 000000000..d88eb4122
--- /dev/null
+++ b/doc/api_samples/os-fping/server-post-req.json
@@ -0,0 +1,16 @@
+{
+ "server" : {
+ "name" : "new-server-test",
+ "imageRef" : "http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b",
+ "flavorRef" : "http://openstack.example.com/openstack/flavors/1",
+ "metadata" : {
+ "My Server Name" : "Apache1"
+ },
+ "personality" : [
+ {
+ "path" : "/etc/banner.txt",
+ "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-fping/server-post-req.xml b/doc/api_samples/os-fping/server-post-req.xml
new file mode 100644
index 000000000..0a3c8bb53
--- /dev/null
+++ b/doc/api_samples/os-fping/server-post-req.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="http://openstack.example.com/openstack/images/70a599e0-31e7-49b7-b260-868f441e862b" flavorRef="http://openstack.example.com/openstack/flavors/1" name="new-server-test">
+ <metadata>
+ <meta key="My Server Name">Apache1</meta>
+ </metadata>
+ <personality>
+ <file path="/etc/banner.txt">
+ ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
+ dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
+ IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
+ c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
+ QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
+ ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
+ dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
+ c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
+ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
+ </file>
+ </personality>
+</server> \ No newline at end of file
diff --git a/doc/api_samples/os-fping/server-post-resp.json b/doc/api_samples/os-fping/server-post-resp.json
new file mode 100644
index 000000000..09d9fb612
--- /dev/null
+++ b/doc/api_samples/os-fping/server-post-resp.json
@@ -0,0 +1,16 @@
+{
+ "server": {
+ "adminPass": "xrDLoBeMD28B",
+ "id": "3f69b6bd-00a8-4636-96ee-650093624304",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/3f69b6bd-00a8-4636-96ee-650093624304",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/3f69b6bd-00a8-4636-96ee-650093624304",
+ "rel": "bookmark"
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-fping/server-post-resp.xml b/doc/api_samples/os-fping/server-post-resp.xml
new file mode 100644
index 000000000..7f84ac03d
--- /dev/null
+++ b/doc/api_samples/os-fping/server-post-resp.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="6ed1d112-6c33-4c8b-9780-e2f978bf5ffd" adminPass="uF9wWxBh3mWL">
+ <metadata/>
+ <atom:link href="http://openstack.example.com/v2/openstack/servers/6ed1d112-6c33-4c8b-9780-e2f978bf5ffd" rel="self"/>
+ <atom:link href="http://openstack.example.com/openstack/servers/6ed1d112-6c33-4c8b-9780-e2f978bf5ffd" rel="bookmark"/>
+</server> \ No newline at end of file
diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters
index e1113a9e7..9562a23aa 100644
--- a/etc/nova/rootwrap.d/compute.filters
+++ b/etc/nova/rootwrap.d/compute.filters
@@ -174,3 +174,9 @@ vgs: CommandFilter, /sbin/vgs, root
# nova/virt/baremetal/volume_driver.py: 'tgtadm', '--lld', 'iscsi', ...
tgtadm: CommandFilter, /usr/sbin/tgtadm, root
+
+# nova/utils.py:read_file_as_root: 'cat', file_path
+# (called from nova/virt/disk/vfs/localfs.py:VFSLocalFS.read_file)
+read_passwd: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/passwd
+read_shadow: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/shadow
+
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 48b0f632f..b66b15852 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -280,7 +280,7 @@ class CloudController(object):
host_services = {}
for service in enabled_services:
zone_hosts.setdefault(service['availability_zone'], [])
- if not service['host'] in zone_hosts[service['availability_zone']]:
+ if service['host'] not in zone_hosts[service['availability_zone']]:
zone_hosts[service['availability_zone']].append(
service['host'])
@@ -407,7 +407,7 @@ class CloudController(object):
def describe_key_pairs(self, context, key_name=None, **kwargs):
key_pairs = self.keypair_api.get_key_pairs(context, context.user_id)
- if not key_name is None:
+ if key_name is not None:
key_pairs = [x for x in key_pairs if x['name'] in key_name]
#If looking for non existent key pair
@@ -527,7 +527,7 @@ class CloudController(object):
def _rule_args_to_dict(self, context, kwargs):
rules = []
- if not 'groups' in kwargs and not 'ip_ranges' in kwargs:
+ if 'groups' not in kwargs and 'ip_ranges' not in kwargs:
rule = self._rule_dict_last_step(context, **kwargs)
if rule:
rules.append(rule)
@@ -991,18 +991,22 @@ class CloudController(object):
def describe_instances(self, context, **kwargs):
# Optional DescribeInstances argument
instance_id = kwargs.get('instance_id', None)
+ filters = kwargs.get('filter', None)
instances = self._enforce_valid_instance_ids(context, instance_id)
return self._format_describe_instances(context,
instance_id=instance_id,
- instance_cache=instances)
+ instance_cache=instances,
+ filter=filters)
def describe_instances_v6(self, context, **kwargs):
# Optional DescribeInstancesV6 argument
instance_id = kwargs.get('instance_id', None)
+ filters = kwargs.get('filter', None)
instances = self._enforce_valid_instance_ids(context, instance_id)
return self._format_describe_instances(context,
instance_id=instance_id,
instance_cache=instances,
+ filter=filters,
use_v6=True)
def _format_describe_instances(self, context, **kwargs):
@@ -1545,11 +1549,11 @@ class CloudController(object):
if attribute != 'launchPermission':
raise exception.EC2APIError(_('attribute not supported: %s')
% attribute)
- if not 'user_group' in kwargs:
+ if 'user_group' not in kwargs:
raise exception.EC2APIError(_('user or group not specified'))
if len(kwargs['user_group']) != 1 and kwargs['user_group'][0] != 'all':
raise exception.EC2APIError(_('only group "all" is supported'))
- if not operation_type in ['add', 'remove']:
+ if operation_type not in ['add', 'remove']:
msg = _('operation_type must be add or remove')
raise exception.EC2APIError(msg)
LOG.audit(_("Updating image %s publicity"), image_id, context=context)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index d812cef18..a76b74324 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -102,7 +102,7 @@ class APIMapper(routes.Mapper):
class ProjectMapper(APIMapper):
def resource(self, member_name, collection_name, **kwargs):
- if not ('parent_resource' in kwargs):
+ if 'parent_resource' not in kwargs:
kwargs['path_prefix'] = '{project_id}/'
else:
parent_resource = kwargs['parent_resource']
diff --git a/nova/api/openstack/compute/contrib/aggregates.py b/nova/api/openstack/compute/contrib/aggregates.py
index 91d138be4..84b0358a3 100644
--- a/nova/api/openstack/compute/contrib/aggregates.py
+++ b/nova/api/openstack/compute/contrib/aggregates.py
@@ -106,7 +106,7 @@ class AggregateController(object):
raise exc.HTTPBadRequest
for key in updates.keys():
- if not key in ["name", "availability_zone"]:
+ if key not in ["name", "availability_zone"]:
raise exc.HTTPBadRequest
try:
diff --git a/nova/api/openstack/compute/contrib/availability_zone.py b/nova/api/openstack/compute/contrib/availability_zone.py
index 6cde5ca64..98c508bd7 100644
--- a/nova/api/openstack/compute/contrib/availability_zone.py
+++ b/nova/api/openstack/compute/contrib/availability_zone.py
@@ -110,7 +110,7 @@ class AvailabilityZoneController(wsgi.Controller):
host_services = {}
for service in enabled_services:
zone_hosts.setdefault(service['availability_zone'], [])
- if not service['host'] in zone_hosts[service['availability_zone']]:
+ if service['host'] not in zone_hosts[service['availability_zone']]:
zone_hosts[service['availability_zone']].append(
service['host'])
diff --git a/nova/api/openstack/compute/contrib/flavorextraspecs.py b/nova/api/openstack/compute/contrib/flavorextraspecs.py
index 84f157b6a..12cc7d9ed 100644
--- a/nova/api/openstack/compute/contrib/flavorextraspecs.py
+++ b/nova/api/openstack/compute/contrib/flavorextraspecs.py
@@ -84,7 +84,7 @@ class FlavorExtraSpecsController(object):
context = req.environ['nova.context']
authorize(context)
self._check_body(body)
- if not id in body:
+ if id not in body:
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)
if len(body) > 1:
diff --git a/nova/api/openstack/compute/contrib/floating_ips_bulk.py b/nova/api/openstack/compute/contrib/floating_ips_bulk.py
index f5b8d24dd..db506a15d 100644
--- a/nova/api/openstack/compute/contrib/floating_ips_bulk.py
+++ b/nova/api/openstack/compute/contrib/floating_ips_bulk.py
@@ -80,13 +80,13 @@ class FloatingIPBulkController(object):
context = req.environ['nova.context']
authorize(context)
- if not 'floating_ips_bulk_create' in body:
+ if 'floating_ips_bulk_create' not in body:
raise webob.exc.HTTPUnprocessableEntity()
params = body['floating_ips_bulk_create']
LOG.debug(params)
- if not 'ip_range' in params:
+ if 'ip_range' not in params:
raise webob.exc.HTTPUnprocessableEntity()
ip_range = params['ip_range']
diff --git a/nova/api/openstack/compute/contrib/simple_tenant_usage.py b/nova/api/openstack/compute/contrib/simple_tenant_usage.py
index 8502e93c4..2313c00ac 100644
--- a/nova/api/openstack/compute/contrib/simple_tenant_usage.py
+++ b/nova/api/openstack/compute/contrib/simple_tenant_usage.py
@@ -159,7 +159,7 @@ class SimpleTenantUsageController(object):
info['uptime'] = delta.days * 24 * 3600 + delta.seconds
- if not info['tenant_id'] in rval:
+ if info['tenant_id'] not in rval:
summary = {}
summary['tenant_id'] = info['tenant_id']
if detailed:
diff --git a/nova/api/openstack/compute/image_metadata.py b/nova/api/openstack/compute/image_metadata.py
index 1a467f3a7..7e78d6324 100644
--- a/nova/api/openstack/compute/image_metadata.py
+++ b/nova/api/openstack/compute/image_metadata.py
@@ -76,7 +76,7 @@ class Controller(object):
expl = _('Incorrect request body format')
raise exc.HTTPBadRequest(explanation=expl)
- if not id in meta:
+ if id not in meta:
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)
if len(meta) > 1:
@@ -105,7 +105,7 @@ class Controller(object):
def delete(self, req, image_id, id):
context = req.environ['nova.context']
image = self._get_image(context, image_id)
- if not id in image['properties']:
+ if id not in image['properties']:
msg = _("Invalid metadata key")
raise exc.HTTPNotFound(explanation=msg)
image['properties'].pop(id)
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index 93a07ec3f..ac4ebd293 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -751,7 +751,7 @@ class Controller(wsgi.Controller):
server_dict = body['server']
password = self._get_server_admin_password(server_dict)
- if not 'name' in server_dict:
+ if 'name' not in server_dict:
msg = _("Server name is not defined")
raise exc.HTTPBadRequest(explanation=msg)
@@ -911,6 +911,8 @@ class Controller(wsgi.Controller):
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
raise exc.HTTPBadRequest(explanation=msg)
+ except exception.ImageNotActive as error:
+ raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.FlavorNotFound as error:
msg = _("Invalid flavorRef provided.")
raise exc.HTTPBadRequest(explanation=msg)
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index a6f255081..f68eff2a7 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -150,7 +150,7 @@ class Request(webob.Request):
Does not do any body introspection, only checks header
"""
- if not "Content-Type" in self.headers:
+ if "Content-Type" not in self.headers:
return None
content_type = self.content_type
diff --git a/nova/availability_zones.py b/nova/availability_zones.py
index 711eee1fa..97faccc9f 100644
--- a/nova/availability_zones.py
+++ b/nova/availability_zones.py
@@ -71,7 +71,7 @@ def get_availability_zones(context):
available_zones = []
for zone in [service['availability_zone'] for service
in enabled_services]:
- if not zone in available_zones:
+ if zone not in available_zones:
available_zones.append(zone)
not_available_zones = []
diff --git a/nova/common/memorycache.py b/nova/common/memorycache.py
index f77b3f51a..86057b6ae 100644
--- a/nova/common/memorycache.py
+++ b/nova/common/memorycache.py
@@ -70,7 +70,7 @@ class Client(object):
def add(self, key, value, time=0, min_compress_len=0):
"""Sets the value for a key if it doesn't exist."""
- if not self.get(key) is None:
+ if self.get(key) is not None:
return False
return self.set(key, value, time, min_compress_len)
diff --git a/nova/compute/vm_mode.py b/nova/compute/vm_mode.py
index 26e5ad8a0..cc1ca6978 100644
--- a/nova/compute/vm_mode.py
+++ b/nova/compute/vm_mode.py
@@ -52,7 +52,7 @@ def get_from_instance(instance):
if mode == "hv":
mode = HVM
- if not mode in ALL:
+ if mode not in ALL:
raise exception.Invalid("Unknown vm mode '%s'" % mode)
return mode
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 61f27f31c..ad7e4f21f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1244,7 +1244,7 @@ def fixed_ip_get_by_network_host(context, network_id, host):
first()
if not result:
- raise exception.FixedIpNotFoundForNetworkHost(network_uuid=network_id,
+ raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id,
host=host)
return result
diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py
index eb5d8016f..28ec613c5 100644
--- a/nova/db/sqlalchemy/session.py
+++ b/nova/db/sqlalchemy/session.py
@@ -228,8 +228,10 @@ from eventlet import db_pool
from eventlet import greenthread
try:
import MySQLdb
+ from MySQLdb.constants import CLIENT as mysql_client_constants
except ImportError:
MySQLdb = None
+ mysql_client_constants = None
from sqlalchemy.exc import DisconnectionError, OperationalError, IntegrityError
import sqlalchemy.interfaces
import sqlalchemy.orm
@@ -482,9 +484,21 @@ def create_engine(sql_connection):
'user': connection_dict.username,
'min_size': CONF.sql_min_pool_size,
'max_size': CONF.sql_max_pool_size,
- 'max_idle': CONF.sql_idle_timeout}
- creator = db_pool.ConnectionPool(MySQLdb, **pool_args)
- engine_args['creator'] = creator.create
+ 'max_idle': CONF.sql_idle_timeout,
+ 'client_flag': mysql_client_constants.FOUND_ROWS}
+
+ pool = db_pool.ConnectionPool(MySQLdb, **pool_args)
+
+ def creator():
+ conn = pool.create()
+ if isinstance(conn, tuple):
+ # NOTE(belliott) eventlet >= 0.10 returns a tuple
+ now, now, conn = conn
+
+ return conn
+
+ engine_args['creator'] = creator
+
else:
engine_args['pool_size'] = CONF.sql_max_pool_size
if CONF.sql_max_overflow is not None:
@@ -550,6 +564,10 @@ class Session(sqlalchemy.orm.session.Session):
def flush(self, *args, **kwargs):
return super(Session, self).flush(*args, **kwargs)
+ @wrap_db_error
+ def execute(self, *args, **kwargs):
+ return super(Session, self).execute(*args, **kwargs)
+
def get_maker(engine, autocommit=True, expire_on_commit=False):
"""Return a SQLAlchemy sessionmaker using the given engine."""
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 1a6bba62f..6eac96c35 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -485,6 +485,8 @@ def get_remote_image_service(context, image_href):
:returns: a tuple of the form (image_service, image_id)
"""
+ # Calling out to another service may take a while, so lets log this
+ LOG.debug(_("fetching image %s from glance") % image_href)
#NOTE(bcwaldon): If image_href doesn't look like a URI, assume its a
# standalone image ID
if '/' not in str(image_href):
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 16714a5ff..01bef4185 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -27,6 +27,7 @@ from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import utils as compute_utils
from nova.compute import vm_states
+from nova.conductor import api as conductor_api
from nova import db
from nova import exception
from nova import notifications
@@ -66,6 +67,7 @@ def handle_schedule_error(context, ex, instance_uuid, request_spec):
notifications.send_update(context, old_ref, new_ref,
service="scheduler")
compute_utils.add_instance_fault_from_exc(context,
+ conductor_api.LocalAPI(),
new_ref, ex, sys.exc_info())
properties = request_spec.get('instance_properties', {})
diff --git a/nova/scheduler/filters/trusted_filter.py b/nova/scheduler/filters/trusted_filter.py
index 302d2b3a8..14f1a37b0 100644
--- a/nova/scheduler/filters/trusted_filter.py
+++ b/nova/scheduler/filters/trusted_filter.py
@@ -269,7 +269,7 @@ class ComputeAttestationCache(object):
def get_host_attestation(self, host):
"""Check host's trust level."""
- if not host in self.compute_nodes:
+ if host not in self.compute_nodes:
self._init_cache_entry(host)
if not self._cache_valid(host):
self._update_cache()
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 23e64cd7c..e6bf1a293 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -26,6 +26,7 @@ import sys
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import utils as compute_utils
from nova.compute import vm_states
+from nova.conductor import api as conductor_api
import nova.context
from nova import db
from nova import exception
@@ -190,6 +191,7 @@ class SchedulerManager(manager.Manager):
notifications.send_update(context, old_ref, new_ref,
service="scheduler")
compute_utils.add_instance_fault_from_exc(context,
+ conductor_api.LocalAPI(),
new_ref, ex, sys.exc_info())
payload = dict(request_spec=request_spec,
diff --git a/nova/service.py b/nova/service.py
index 2daceba80..3d556a202 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -621,7 +621,7 @@ class WSGIService(object):
"""
fl = '%s_manager' % self.name
- if not fl in CONF:
+ if fl not in CONF:
return None
manager_class_name = CONF.get(fl, None)
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index a00dceff1..c60a0148e 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -824,6 +824,19 @@ class CloudTestCase(test.TestCase):
self.cloud.describe_instances, self.context,
instance_id=[instance_id])
+ def test_describe_instances_with_filters(self):
+ # Makes sure describe_instances works and filters results.
+ filters = {'filter': [{'name': 'test',
+ 'value': ['a', 'b']},
+ {'name': 'another_test',
+ 'value': 'a string'}]}
+
+ self._stub_instance_get_with_fixed_ips('get_all')
+ self._stub_instance_get_with_fixed_ips('get')
+
+ result = self.cloud.describe_instances(self.context, **filters)
+ self.assertEqual(result, {'reservationSet': []})
+
def test_describe_instances_sorting(self):
# Makes sure describe_instances works and is sorted as expected.
self.flags(use_ipv6=True)
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
index 5328ec2ee..a3745d573 100644
--- a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
+++ b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
@@ -18,10 +18,9 @@
import webob
from nova.api.openstack.compute.contrib import flavorextraspecs
-from nova.api.openstack import wsgi
+import nova.db
from nova import test
from nova.tests.api.openstack import fakes
-import nova.wsgi
def return_create_flavor_extra_specs(context, flavor_id, extra_specs):
@@ -174,14 +173,6 @@ class FlavorsExtraSpecsXMLSerializerTest(test.TestCase):
text = serializer.serialize(dict(extra_specs={"key1": "value1"}))
self.assertEqual(text, expected)
- def test_deserializer(self):
- deserializer = wsgi.XMLDeserializer()
- expected = dict(extra_specs={"key1": "value1"})
- intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
- '<extra_specs><key1>value1</key1></extra_specs>')
- result = deserializer.deserialize(intext)['body']
- self.assertEqual(result, expected)
-
def test_show_update_serializer(self):
serializer = flavorextraspecs.ExtraSpecTemplate()
expected = ("<?xml version='1.0' encoding='UTF-8'?>\n"
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 03fc87ac5..3ef98b902 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -372,7 +372,7 @@ def create_info_cache(nw_cache):
def get_fake_uuid(token=0):
- if not token in FAKE_UUIDS:
+ if token not in FAKE_UUIDS:
FAKE_UUIDS[token] = str(uuid.uuid4())
return FAKE_UUIDS[token]
@@ -399,12 +399,12 @@ def fake_instance_get_all_by_filters(num_servers=5, **kwargs):
server = stub_instance(id=i + 1, uuid=uuid,
**kwargs)
servers_list.append(server)
- if not marker is None and uuid == marker:
+ if marker is not None and uuid == marker:
found_marker = True
servers_list = []
- if not marker is None and not found_marker:
+ if marker is not None and not found_marker:
raise exc.MarkerNotFound(marker=marker)
- if not limit is None:
+ if limit is not None:
servers_list = servers_list[:limit]
return servers_list
return _return_servers
diff --git a/nova/tests/compute/test_resource_tracker.py b/nova/tests/compute/test_resource_tracker.py
index 53d92a13f..eaa0df5bf 100644
--- a/nova/tests/compute/test_resource_tracker.py
+++ b/nova/tests/compute/test_resource_tracker.py
@@ -391,7 +391,7 @@ class BaseTrackerTestCase(BaseTestCase):
if tracker is None:
tracker = self.tracker
- if not field in tracker.compute_node:
+ if field not in tracker.compute_node:
raise test.TestingException(
"'%(field)s' not in compute node." % locals())
x = tracker.compute_node[field]
diff --git a/nova/tests/fakeguestfs.py b/nova/tests/fakeguestfs.py
index 33ca49c33..ff006db68 100644
--- a/nova/tests/fakeguestfs.py
+++ b/nova/tests/fakeguestfs.py
@@ -50,7 +50,7 @@ class GuestFS(object):
self.mounts.append((options, device, mntpoint))
def mkdir_p(self, path):
- if not path in self.files:
+ if path not in self.files:
self.files[path] = {
"isdir": True,
"gid": 100,
@@ -59,7 +59,7 @@ class GuestFS(object):
}
def read_file(self, path):
- if not path in self.files:
+ if path not in self.files:
self.files[path] = {
"isdir": False,
"content": "Hello World",
@@ -71,7 +71,7 @@ class GuestFS(object):
return self.files[path]["content"]
def write(self, path, content):
- if not path in self.files:
+ if path not in self.files:
self.files[path] = {
"isdir": False,
"content": "Hello World",
@@ -83,7 +83,7 @@ class GuestFS(object):
self.files[path]["content"] = content
def write_append(self, path, content):
- if not path in self.files:
+ if path not in self.files:
self.files[path] = {
"isdir": False,
"content": "Hello World",
@@ -95,13 +95,13 @@ class GuestFS(object):
self.files[path]["content"] = self.files[path]["content"] + content
def stat(self, path):
- if not path in self.files:
+ if path not in self.files:
raise RuntimeError("No such file: " + path)
return self.files[path]["mode"]
def chown(self, uid, gid, path):
- if not path in self.files:
+ if path not in self.files:
raise RuntimeError("No such file: " + path)
if uid != -1:
@@ -110,7 +110,7 @@ class GuestFS(object):
self.files[path]["gid"] = gid
def chmod(self, mode, path):
- if not path in self.files:
+ if path not in self.files:
raise RuntimeError("No such file: " + path)
self.files[path]["mode"] = mode
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index a072b3128..958a5500b 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -155,7 +155,7 @@ class TestOpenStackClient(object):
LOG.debug(_("%(relative_uri)s => code %(http_status)s") % locals())
if check_response_status:
- if not http_status in check_response_status:
+ if http_status not in check_response_status:
if http_status == 404:
raise OpenStackApiNotFoundException(response=response)
elif http_status == 401:
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl
new file mode 100644
index 000000000..c94595cad
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<extra_specs>
+ <key1>%(value1)s</key1>
+ <key2>%(value2)s</key2>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl
new file mode 100644
index 000000000..1008b5bb0
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>%(value2)s</key2>
+ <key1>%(value1)s</key1>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl
new file mode 100644
index 000000000..e3de59a34
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">%(value1)s</extra_spec>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl
new file mode 100644
index 000000000..1008b5bb0
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>%(value2)s</key2>
+ <key1>%(value1)s</key1>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl
new file mode 100644
index 000000000..6421e5959
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <key1>%(value1)s</key1>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl
new file mode 100644
index 000000000..e3de59a34
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">%(value1)s</extra_spec>
diff --git a/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.json.tpl b/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.json.tpl
new file mode 100644
index 000000000..f3b222c39
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.json.tpl
@@ -0,0 +1,7 @@
+{
+ "server": {
+ "alive": false,
+ "id": "%(uuid)s",
+ "project_id": "openstack"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.xml.tpl b/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.xml.tpl
new file mode 100644
index 000000000..758519b60
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/fping-get-details-resp.xml.tpl
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<server>
+ <project_id>openstack</project_id>
+ <id>%(uuid)s</id>
+ <alive>False</alive>
+</server>
diff --git a/nova/tests/integrated/api_samples/os-fping/fping-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-fping/fping-get-resp.json.tpl
new file mode 100644
index 000000000..b33e80668
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/fping-get-resp.json.tpl
@@ -0,0 +1,9 @@
+{
+ "servers": [
+ {
+ "alive": false,
+ "id": "%(uuid)s",
+ "project_id": "openstack"
+ }
+ ]
+}
diff --git a/nova/tests/integrated/api_samples/os-fping/fping-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-fping/fping-get-resp.xml.tpl
new file mode 100644
index 000000000..290ad6ca6
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/fping-get-resp.xml.tpl
@@ -0,0 +1,8 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<servers>
+ <server>
+ <project_id>openstack</project_id>
+ <id>%(uuid)s</id>
+ <alive>False</alive>
+ </server>
+</servers>
diff --git a/nova/tests/integrated/api_samples/os-fping/server-post-req.json.tpl b/nova/tests/integrated/api_samples/os-fping/server-post-req.json.tpl
new file mode 100644
index 000000000..d3916d1aa
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/server-post-req.json.tpl
@@ -0,0 +1,16 @@
+{
+ "server" : {
+ "name" : "new-server-test",
+ "imageRef" : "%(host)s/openstack/images/%(image_id)s",
+ "flavorRef" : "%(host)s/openstack/flavors/1",
+ "metadata" : {
+ "My Server Name" : "Apache1"
+ },
+ "personality" : [
+ {
+ "path" : "/etc/banner.txt",
+ "contents" : "ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBpdCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5kIGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVsc2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4gQnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRoZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlvdSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vyc2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6b25zLiINCg0KLVJpY2hhcmQgQmFjaA=="
+ }
+ ]
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-fping/server-post-req.xml.tpl b/nova/tests/integrated/api_samples/os-fping/server-post-req.xml.tpl
new file mode 100644
index 000000000..f92614984
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/server-post-req.xml.tpl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<server xmlns="http://docs.openstack.org/compute/api/v1.1" imageRef="%(host)s/openstack/images/%(image_id)s" flavorRef="%(host)s/openstack/flavors/1" name="new-server-test">
+ <metadata>
+ <meta key="My Server Name">Apache1</meta>
+ </metadata>
+ <personality>
+ <file path="/etc/banner.txt">
+ ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp
+ dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k
+ IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs
+ c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g
+ QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo
+ ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv
+ dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy
+ c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6
+ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
+ </file>
+ </personality>
+</server>
diff --git a/nova/tests/integrated/api_samples/os-fping/server-post-resp.json.tpl b/nova/tests/integrated/api_samples/os-fping/server-post-resp.json.tpl
new file mode 100644
index 000000000..d5f030c87
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/server-post-resp.json.tpl
@@ -0,0 +1,16 @@
+{
+ "server": {
+ "adminPass": "%(password)s",
+ "id": "%(id)s",
+ "links": [
+ {
+ "href": "%(host)s/v2/openstack/servers/%(uuid)s",
+ "rel": "self"
+ },
+ {
+ "href": "%(host)s/openstack/servers/%(uuid)s",
+ "rel": "bookmark"
+ }
+ ]
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-fping/server-post-resp.xml.tpl b/nova/tests/integrated/api_samples/os-fping/server-post-resp.xml.tpl
new file mode 100644
index 000000000..3bb13e69b
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-fping/server-post-resp.xml.tpl
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<server xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://docs.openstack.org/compute/api/v1.1" id="%(id)s" adminPass="%(password)s">
+ <metadata/>
+ <atom:link href="%(host)s/v2/openstack/servers/%(uuid)s" rel="self"/>
+ <atom:link href="%(host)s/openstack/servers/%(uuid)s" rel="bookmark"/>
+</server>
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index f17dc025f..90e9a806e 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -58,7 +58,7 @@ def generate_new_element(items, prefix, numeric=False):
candidate = prefix + generate_random_numeric(8)
else:
candidate = prefix + generate_random_alphanumeric(8)
- if not candidate in items:
+ if candidate not in items:
return candidate
LOG.debug("Random collision on %s" % candidate)
diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py
index 080e4e92b..21a23d94e 100644
--- a/nova/tests/integrated/test_api_samples.py
+++ b/nova/tests/integrated/test_api_samples.py
@@ -27,6 +27,7 @@ from lxml import etree
from nova.api.metadata import password
from nova.api.openstack.compute.contrib import coverage_ext
+from nova.api.openstack.compute.contrib import fping
# Import extensions to pull in osapi_compute_extension CONF option used below.
from nova.cloudpipe import pipelib
from nova import context
@@ -43,10 +44,12 @@ from nova.openstack.common import timeutils
import nova.quota
from nova.scheduler import driver
from nova import test
+from nova.tests.api.openstack.compute.contrib import test_fping
from nova.tests.baremetal.db import base as bm_db_base
from nova.tests import fake_network
from nova.tests.image import fake
from nova.tests.integrated import integrated_helpers
+from nova import utils
CONF = cfg.CONF
CONF.import_opt('allow_resize_to_same_host', 'nova.compute.api')
@@ -377,9 +380,7 @@ class ApiSamplesTrap(ApiSampleTestBase):
do_not_approve_additions.append('os-config-drive')
do_not_approve_additions.append('os-create-server-ext')
do_not_approve_additions.append('os-flavor-access')
- do_not_approve_additions.append('os-flavor-extra-specs')
do_not_approve_additions.append('os-floating-ip-dns')
- do_not_approve_additions.append('os-fping')
do_not_approve_additions.append('os-hypervisors')
do_not_approve_additions.append('os-networks')
do_not_approve_additions.append('os-services')
@@ -2712,4 +2713,90 @@ class InstanceUsageAuditLogJsonTest(ApiSampleTestBase):
class InstanceUsageAuditLogXmlTest(InstanceUsageAuditLogJsonTest):
+ ctype = "xml"
+
+
+class FlavorExtraSpecsSampleJsonTests(ApiSampleTestBase):
+ extension_name = ("nova.api.openstack.compute.contrib.flavorextraspecs."
+ "Flavorextraspecs")
+
+ def _flavor_extra_specs_create(self):
+ subs = {'value1': 'value1',
+ 'value2': 'value2'
+ }
+ response = self._do_post('flavors/1/os-extra_specs',
+ 'flavor-extra-specs-create-req', subs)
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-create-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_get(self):
+ subs = {'value1': 'value1'}
+ self._flavor_extra_specs_create()
+ response = self._do_get('flavors/1/os-extra_specs/key1')
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-get-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_list(self):
+ subs = {'value1': 'value1',
+ 'value2': 'value2'
+ }
+ self._flavor_extra_specs_create()
+ response = self._do_get('flavors/1/os-extra_specs')
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-list-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_create(self):
+ return self._flavor_extra_specs_create()
+
+ def test_flavor_extra_specs_update(self):
+ subs = {'value1': 'new_value1'}
+ self._flavor_extra_specs_create()
+ response = self._do_put('flavors/1/os-extra_specs/key1',
+ 'flavor-extra-specs-update-req', subs)
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-update-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_delete(self):
+ self._flavor_extra_specs_create()
+ response = self._do_delete('flavors/1/os-extra_specs/key1')
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.read(), '')
+
+
+class FlavorExtraSpecsSampleXmlTests(FlavorExtraSpecsSampleJsonTests):
+ ctype = 'xml'
+
+
+class FpingSampleJsonTests(ServersSampleBase):
+ extension_name = ("nova.api.openstack.compute.contrib.fping.Fping")
+
+ def setUp(self):
+ super(FpingSampleJsonTests, self).setUp()
+
+ def fake_check_fping(self):
+ pass
+ self.stubs.Set(utils, "execute", test_fping.execute)
+ self.stubs.Set(fping.FpingController, "check_fping",
+ fake_check_fping)
+
+ def test_get_fping(self):
+ self._post_server()
+ response = self._do_get('os-fping')
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('fping-get-resp', subs, response)
+
+ def test_get_fping_details(self):
+ uuid = self._post_server()
+ response = self._do_get('os-fping/%s' % (uuid))
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ return self._verify_response('fping-get-details-resp', subs, response)
+
+
+class FpingSampleXmlTests(FpingSampleJsonTests):
ctype = 'xml'
diff --git a/nova/tests/scheduler/test_chance_scheduler.py b/nova/tests/scheduler/test_chance_scheduler.py
index 76fba900d..dcbe86f75 100644
--- a/nova/tests/scheduler/test_chance_scheduler.py
+++ b/nova/tests/scheduler/test_chance_scheduler.py
@@ -25,6 +25,7 @@ import mox
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import utils as compute_utils
from nova.compute import vm_states
+from nova.conductor import api as conductor_api
from nova import context
from nova import db
from nova import exception
@@ -134,7 +135,8 @@ class ChanceSchedulerTestCase(test_scheduler.SchedulerTestCase):
{'vm_state': vm_states.ERROR,
'task_state': None}).AndReturn(({}, {}))
compute_utils.add_instance_fault_from_exc(ctxt,
- new_ref, mox.IsA(exception.NoValidHost), mox.IgnoreArg())
+ mox.IsA(conductor_api.LocalAPI), new_ref,
+ mox.IsA(exception.NoValidHost), mox.IgnoreArg())
self.mox.ReplayAll()
self.driver.schedule_run_instance(
diff --git a/nova/tests/scheduler/test_filter_scheduler.py b/nova/tests/scheduler/test_filter_scheduler.py
index 2bd2cb85b..ff3a00f22 100644
--- a/nova/tests/scheduler/test_filter_scheduler.py
+++ b/nova/tests/scheduler/test_filter_scheduler.py
@@ -21,6 +21,7 @@ import mox
from nova.compute import instance_types
from nova.compute import utils as compute_utils
from nova.compute import vm_states
+from nova.conductor import api as conductor_api
from nova import context
from nova import db
from nova import exception
@@ -62,7 +63,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
uuid, {'vm_state': vm_states.ERROR, 'task_state':
None}).AndReturn(({}, {}))
compute_utils.add_instance_fault_from_exc(fake_context,
- new_ref, mox.IsA(exception.NoValidHost), mox.IgnoreArg())
+ mox.IsA(conductor_api.LocalAPI), new_ref,
+ mox.IsA(exception.NoValidHost), mox.IgnoreArg())
self.mox.ReplayAll()
sched.schedule_run_instance(
fake_context, request_spec, None, None, None, None, {})
@@ -92,7 +94,8 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
uuid, {'vm_state': vm_states.ERROR, 'task_state':
None}).AndReturn(({}, {}))
compute_utils.add_instance_fault_from_exc(fake_context,
- new_ref, mox.IsA(exception.NoValidHost), mox.IgnoreArg())
+ mox.IsA(conductor_api.LocalAPI), new_ref,
+ mox.IsA(exception.NoValidHost), mox.IgnoreArg())
self.mox.ReplayAll()
sched.schedule_run_instance(
fake_context, request_spec, None, None, None, None, {})
diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py
index eb4c3864f..142d8ea0e 100644
--- a/nova/tests/scheduler/test_scheduler.py
+++ b/nova/tests/scheduler/test_scheduler.py
@@ -26,10 +26,12 @@ from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import utils as compute_utils
from nova.compute import vm_states
+from nova.conductor import api as conductor_api
from nova import context
from nova import db
from nova import exception
from nova.openstack.common import jsonutils
+from nova.openstack.common.notifier import api as notifier
from nova.openstack.common import rpc
from nova.scheduler import driver
from nova.scheduler import manager
@@ -187,7 +189,8 @@ class SchedulerManagerTestCase(test.TestCase):
fake_instance_uuid,
{"vm_state": vm_states.ERROR,
"task_state": None}).AndReturn((inst, inst))
- compute_utils.add_instance_fault_from_exc(self.context, new_ref,
+ compute_utils.add_instance_fault_from_exc(self.context,
+ mox.IsA(conductor_api.LocalAPI), new_ref,
mox.IsA(exception.NoValidHost), mox.IgnoreArg())
self.mox.ReplayAll()
@@ -221,7 +224,8 @@ class SchedulerManagerTestCase(test.TestCase):
fake_instance_uuid,
{"vm_state": vm_states.ACTIVE, "task_state": None}).AndReturn(
(inst, inst))
- compute_utils.add_instance_fault_from_exc(self.context, new_ref,
+ compute_utils.add_instance_fault_from_exc(self.context,
+ mox.IsA(conductor_api.LocalAPI), new_ref,
mox.IsA(exception.NoValidHost), mox.IgnoreArg())
self.mox.ReplayAll()
@@ -258,7 +262,8 @@ class SchedulerManagerTestCase(test.TestCase):
fake_instance_uuid,
{"vm_state": vm_states.ERROR,
"task_state": None}).AndReturn((inst, inst))
- compute_utils.add_instance_fault_from_exc(self.context, new_ref,
+ compute_utils.add_instance_fault_from_exc(self.context,
+ mox.IsA(conductor_api.LocalAPI), new_ref,
mox.IsA(test.TestingException), mox.IgnoreArg())
self.mox.ReplayAll()
@@ -266,6 +271,25 @@ class SchedulerManagerTestCase(test.TestCase):
self.assertRaises(test.TestingException, self.manager.prep_resize,
**kwargs)
+ def test_set_vm_state_and_notify_adds_instance_fault(self):
+ request = {'instance_properties': {'uuid': 'fake-uuid'}}
+ updates = {'vm_state': 'foo'}
+ fake_inst = {'uuid': 'fake-uuid'}
+
+ self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
+ self.mox.StubOutWithMock(db, 'instance_fault_create')
+ self.mox.StubOutWithMock(notifier, 'notify')
+ db.instance_update_and_get_original(self.context, 'fake-uuid',
+ updates).AndReturn((None,
+ fake_inst))
+ db.instance_fault_create(self.context, mox.IgnoreArg())
+ notifier.notify(self.context, mox.IgnoreArg(), 'scheduler.foo',
+ notifier.ERROR, mox.IgnoreArg())
+ self.mox.ReplayAll()
+
+ self.manager._set_vm_state_and_notify('foo', {'vm_state': 'foo'},
+ self.context, None, request)
+
class SchedulerTestCase(test.TestCase):
"""Test case for base scheduler driver class."""
@@ -620,6 +644,24 @@ class SchedulerTestCase(test.TestCase):
block_migration=block_migration,
disk_over_commit=disk_over_commit)
+ def test_handle_schedule_error_adds_instance_fault(self):
+ instance = {'uuid': 'fake-uuid'}
+ self.mox.StubOutWithMock(db, 'instance_update_and_get_original')
+ self.mox.StubOutWithMock(db, 'instance_fault_create')
+ self.mox.StubOutWithMock(notifier, 'notify')
+ db.instance_update_and_get_original(self.context, instance['uuid'],
+ mox.IgnoreArg()).AndReturn(
+ (None, instance))
+ db.instance_fault_create(self.context, mox.IgnoreArg())
+ notifier.notify(self.context, mox.IgnoreArg(),
+ 'scheduler.run_instance',
+ notifier.ERROR, mox.IgnoreArg())
+ self.mox.ReplayAll()
+
+ driver.handle_schedule_error(self.context,
+ exception.NoValidHost('test'),
+ instance['uuid'], {})
+
class SchedulerDriverBaseTestCase(SchedulerTestCase):
"""Test cases for base scheduler driver class methods
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index 4485be4f9..684f9fded 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -1510,6 +1510,25 @@ class MigrationTestCase(test.TestCase):
self.assertEqual(migration['instance_uuid'], instance['uuid'])
+class TestFixedIPGetByNetworkHost(test.TestCase):
+ def test_not_found_exception(self):
+ ctxt = context.get_admin_context()
+
+ self.assertRaises(
+ exception.FixedIpNotFoundForNetworkHost,
+ db.fixed_ip_get_by_network_host,
+ ctxt, 1, 'ignore')
+
+ def test_fixed_ip_found(self):
+ ctxt = context.get_admin_context()
+ db.fixed_ip_create(ctxt, dict(network_id=1, host='host'))
+
+ fip = db.fixed_ip_get_by_network_host(ctxt, 1, 'host')
+
+ self.assertEquals(1, fip['network_id'])
+ self.assertEquals('host', fip['host'])
+
+
class TestIpAllocation(test.TestCase):
def setUp(self):
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 46c244ff1..06db4f5ff 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -3812,7 +3812,7 @@ class IptablesFirewallTestCase(test.TestCase):
in_rules = filter(lambda l: not l.startswith('#'),
self.in_rules)
for rule in in_rules:
- if not 'nova' in rule:
+ if 'nova' not in rule:
self.assertTrue(rule in self.out_rules,
'Rule went missing: %s' % rule)
@@ -4213,7 +4213,7 @@ class LibvirtUtilsTestCase(test.TestCase):
def test_pick_disk_driver_name(self):
type_map = {'kvm': ([True, 'qemu'], [False, 'qemu'], [None, 'qemu']),
'qemu': ([True, 'qemu'], [False, 'qemu'], [None, 'qemu']),
- 'xen': ([True, 'phy'], [False, 'file'], [None, 'file']),
+ 'xen': ([True, 'phy'], [False, 'tap'], [None, 'tap']),
'uml': ([True, None], [False, None], [None, None]),
'lxc': ([True, None], [False, None], [None, None])}
diff --git a/nova/tests/test_sqlalchemy.py b/nova/tests/test_sqlalchemy.py
index f79d607f8..5c7f4450b 100644
--- a/nova/tests/test_sqlalchemy.py
+++ b/nova/tests/test_sqlalchemy.py
@@ -22,8 +22,14 @@ try:
except ImportError:
MySQLdb = None
+from sqlalchemy import Column, MetaData, Table, UniqueConstraint
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import DateTime, Integer
+
from nova import context
+from nova.db.sqlalchemy import models
from nova.db.sqlalchemy import session
+from nova import exception
from nova import test
@@ -64,3 +70,60 @@ class DbPoolTestCase(test.TestCase):
self.assertEqual(info['kwargs']['max_idle'], 11)
self.assertEqual(info['kwargs']['min_size'], 21)
self.assertEqual(info['kwargs']['max_size'], 42)
+
+
+BASE = declarative_base()
+_TABLE_NAME = '__tmp__test__tmp__'
+
+
+class TmpTable(BASE, models.NovaBase):
+ __tablename__ = _TABLE_NAME
+ id = Column(Integer, primary_key=True)
+ foo = Column(Integer)
+
+
+class SessionErrorWrapperTestCase(test.TestCase):
+ def setUp(self):
+ super(SessionErrorWrapperTestCase, self).setUp()
+ meta = MetaData()
+ meta.bind = session.get_engine()
+ test_table = Table(_TABLE_NAME, meta,
+ Column('id', Integer, primary_key=True,
+ nullable=False),
+ Column('deleted', Integer, default=0),
+ Column('deleted_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('created_at', DateTime),
+ Column('foo', Integer),
+ UniqueConstraint('foo', name='uniq_foo'))
+ test_table.create()
+
+ def tearDown(self):
+ super(SessionErrorWrapperTestCase, self).tearDown()
+ meta = MetaData()
+ meta.bind = session.get_engine()
+ test_table = Table(_TABLE_NAME, meta, autoload=True)
+ test_table.drop()
+
+ def test_flush_wrapper(self):
+ tbl = TmpTable()
+ tbl.update({'foo': 10})
+ tbl.save()
+
+ tbl2 = TmpTable()
+ tbl2.update({'foo': 10})
+ self.assertRaises(exception.DBDuplicateEntry, tbl2.save)
+
+ def test_execute_wrapper(self):
+ _session = session.get_session()
+ with _session.begin():
+ for i in [10, 20]:
+ tbl = TmpTable()
+ tbl.update({'foo': i})
+ tbl.save(session=_session)
+
+ method = _session.query(TmpTable).\
+ filter_by(foo=10).\
+ update
+ self.assertRaises(exception.DBDuplicateEntry,
+ method, {'foo': 20})
diff --git a/nova/tests/test_virt_disk_vfs_localfs.py b/nova/tests/test_virt_disk_vfs_localfs.py
index af4571dd2..096a75964 100644
--- a/nova/tests/test_virt_disk_vfs_localfs.py
+++ b/nova/tests/test_virt_disk_vfs_localfs.py
@@ -46,7 +46,7 @@ def fake_execute(*args, **kwargs):
elif args[0] == "chown":
owner = args[1]
path = args[2]
- if not path in files:
+ if path not in files:
raise Exception("No such file: " + path)
sep = owner.find(':')
@@ -72,7 +72,7 @@ def fake_execute(*args, **kwargs):
elif args[0] == "chgrp":
group = args[1]
path = args[2]
- if not path in files:
+ if path not in files:
raise Exception("No such file: " + path)
if group == "users":
@@ -83,13 +83,13 @@ def fake_execute(*args, **kwargs):
elif args[0] == "chmod":
mode = args[1]
path = args[2]
- if not path in files:
+ if path not in files:
raise Exception("No such file: " + path)
files[path]["mode"] = int(mode, 8)
elif args[0] == "cat":
path = args[1]
- if not path in files:
+ if path not in files:
files[path] = {
"content": "Hello World",
"gid": 100,
@@ -104,7 +104,7 @@ def fake_execute(*args, **kwargs):
else:
path = args[1]
append = False
- if not path in files:
+ if path not in files:
files[path] = {
"content": "Hello World",
"gid": 100,
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 067e28a13..c480d5c5f 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -19,7 +19,6 @@
import ast
import base64
import contextlib
-import cPickle as pickle
import functools
import os
import re
@@ -48,6 +47,7 @@ from nova.virt.xenapi import agent
from nova.virt.xenapi import driver as xenapi_conn
from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import host
+from nova.virt.xenapi.imageupload import glance
from nova.virt.xenapi import pool
from nova.virt.xenapi import pool_states
from nova.virt.xenapi import vm_utils
@@ -431,15 +431,29 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
{'task_state': task_states.IMAGE_UPLOADING,
'expected_state': task_states.IMAGE_PENDING_UPLOAD}}]
func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
+ image_id = "my_snapshot_id"
stubs.stubout_instance_snapshot(self.stubs)
stubs.stubout_is_snapshot(self.stubs)
# Stubbing out firewall driver as previous stub sets alters
# xml rpc result parsing
stubs.stubout_firewall_driver(self.stubs, self.conn)
+
instance = self._create_instance()
- image_id = "my_snapshot_id"
+ self.fake_upload_called = False
+
+ def fake_image_upload(_self, ctx, session, inst, vdi_uuids,
+ img_id):
+ self.fake_upload_called = True
+ self.assertEqual(ctx, self.context)
+ self.assertEqual(inst, instance)
+ self.assertTrue(isinstance(vdi_uuids, list))
+ self.assertEqual(img_id, image_id)
+
+ self.stubs.Set(glance.GlanceStore, 'upload_image',
+ fake_image_upload)
+
self.conn.snapshot(self.context, instance, image_id,
func_call_matcher.call)
@@ -469,6 +483,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
name_label = vdi_rec["name_label"]
self.assert_(not name_label.endswith('snapshot'))
+ self.assertTrue(self.fake_upload_called)
+
def create_vm_record(self, conn, os_type, name):
instances = conn.list_instances()
self.assertEquals(instances, [name])
@@ -579,7 +595,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
def _check_vdis(self, start_list, end_list):
for vdi_ref in end_list:
- if not vdi_ref in start_list:
+ if vdi_ref not in start_list:
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
# If the cache is turned on then the base disk will be
# there even after the cleanup
@@ -1933,7 +1949,7 @@ class XenAPIDom0IptablesFirewallTestCase(stubs.XenAPITestBase):
in_rules = filter(lambda l: not l.startswith('#'),
self._in_rules)
for rule in in_rules:
- if not 'nova' in rule:
+ if 'nova' not in rule:
self.assertTrue(rule in self._out_rules,
'Rule went missing: %s' % rule)
@@ -2574,54 +2590,6 @@ class SwapXapiHostTestCase(test.TestCase):
"http://someserver", 'otherserver'))
-class VmUtilsTestCase(test.TestCase):
- """Unit tests for xenapi utils."""
-
- def test_upload_image(self):
- 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(dict):
- def __init__(self):
- super(FakeInstance, self).__init__({
- 'auto_disk_config': 'auto disk config',
- 'os_type': 'os type'})
-
- def __missing__(self, item):
- return "whatever"
-
- class FakeSession(object):
- def call_plugin(session_self, service, command, kwargs):
- self.kwargs = kwargs
-
- def call_plugin_serialized(session_self, service, command, *args,
- **kwargs):
- self.kwargs = kwargs
-
- def fake_dumps(thing):
- return thing
-
- 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['properties']
- # Inheritance happens in another place, now
- expected = dict(auto_disk_config='auto disk config',
- os_type='os type')
- self.assertEquals(expected, actual)
-
-
class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
"""Unit tests for live_migration."""
def setUp(self):
diff --git a/nova/tests/virt/xenapi/imageupload/__init__.py b/nova/tests/virt/xenapi/imageupload/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/tests/virt/xenapi/imageupload/__init__.py
diff --git a/nova/tests/virt/xenapi/imageupload/test_glance.py b/nova/tests/virt/xenapi/imageupload/test_glance.py
new file mode 100644
index 000000000..b0518228d
--- /dev/null
+++ b/nova/tests/virt/xenapi/imageupload/test_glance.py
@@ -0,0 +1,74 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+import mox
+
+from nova import context
+from nova import test
+from nova.virt.xenapi.imageupload import glance
+from nova.virt.xenapi import vm_utils
+
+
+class TestGlanceStore(test.TestCase):
+ def setUp(self):
+ super(TestGlanceStore, self).setUp()
+ self.store = glance.GlanceStore()
+ self.mox = mox.Mox()
+
+ def tearDown(self):
+ super(TestGlanceStore, self).tearDown()
+
+ def test_upload_image(self):
+ glance_host = '0.1.2.3'
+ glance_port = 8143
+ glance_use_ssl = False
+ sr_path = '/fake/sr/path'
+ self.flags(glance_host=glance_host)
+ self.flags(glance_port=glance_port)
+ self.flags(glance_api_insecure=glance_use_ssl)
+
+ def fake_get_sr_path(*_args, **_kwargs):
+ return sr_path
+
+ self.stubs.Set(vm_utils, 'get_sr_path', fake_get_sr_path)
+
+ ctx = context.RequestContext('user', 'project', auth_token='foobar')
+ properties = {
+ 'auto_disk_config': True,
+ 'os_type': 'default',
+ }
+ image_id = 'fake_image_uuid'
+ vdi_uuids = ['fake_vdi_uuid']
+ instance = {'uuid': 'blah'}
+ instance.update(properties)
+
+ params = {'vdi_uuids': vdi_uuids,
+ 'image_id': image_id,
+ 'glance_host': glance_host,
+ 'glance_port': glance_port,
+ 'glance_use_ssl': glance_use_ssl,
+ 'sr_path': sr_path,
+ 'auth_token': 'foobar',
+ 'properties': properties}
+ session = self.mox.CreateMockAnything()
+ session.call_plugin_serialized('glance', 'upload_vhd', **params)
+ self.mox.ReplayAll()
+
+ self.store.upload_image(ctx, session, instance, vdi_uuids, image_id)
+
+ self.mox.VerifyAll()
diff --git a/nova/utils.py b/nova/utils.py
index 7ceb16563..83bf55583 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -628,7 +628,7 @@ class DynamicLoopingCall(LoopingCallBase):
if not self._running:
break
- if not periodic_interval_max is None:
+ if periodic_interval_max is not None:
idle = min(idle, periodic_interval_max)
LOG.debug(_('Periodic task processor sleeping for %.02f '
'seconds'), idle)
diff --git a/nova/virt/baremetal/driver.py b/nova/virt/baremetal/driver.py
index 9904fdcd4..631a9a8c4 100644
--- a/nova/virt/baremetal/driver.py
+++ b/nova/virt/baremetal/driver.py
@@ -140,7 +140,7 @@ class BareMetalDriver(driver.ComputeDriver):
keyval[0] = keyval[0].strip()
keyval[1] = keyval[1].strip()
extra_specs[keyval[0]] = keyval[1]
- if not 'cpu_arch' in extra_specs:
+ if 'cpu_arch' not in extra_specs:
LOG.warning(
_('cpu_arch is not found in instance_type_extra_specs'))
extra_specs['cpu_arch'] = ''
diff --git a/nova/virt/baremetal/volume_driver.py b/nova/virt/baremetal/volume_driver.py
index 0a05dfedd..e92325b97 100644
--- a/nova/virt/baremetal/volume_driver.py
+++ b/nova/virt/baremetal/volume_driver.py
@@ -210,7 +210,7 @@ class LibvirtVolumeDriver(VolumeDriver):
def _volume_driver_method(self, method_name, connection_info,
*args, **kwargs):
driver_type = connection_info.get('driver_volume_type')
- if not driver_type in self.volume_drivers:
+ if driver_type not in self.volume_drivers:
raise exception.VolumeDriverNotFound(driver_type=driver_type)
driver = self.volume_drivers[driver_type]
method = getattr(driver, method_name)
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 04eeded72..5a5bb7b13 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -125,7 +125,7 @@ class FakeDriver(driver.ComputeDriver):
self.instances[name] = fake_instance
def snapshot(self, context, instance, name, update_task_state):
- if not instance['name'] in self.instances:
+ if instance['name'] not in self.instances:
raise exception.InstanceNotRunning(instance_id=instance['uuid'])
update_task_state(task_state=task_states.IMAGE_UPLOADING)
@@ -209,7 +209,7 @@ class FakeDriver(driver.ComputeDriver):
def attach_volume(self, connection_info, instance, mountpoint):
"""Attach the disk to the instance at mountpoint using info."""
instance_name = instance['name']
- if not instance_name in self._mounts:
+ if instance_name not in self._mounts:
self._mounts[instance_name] = {}
self._mounts[instance_name][mountpoint] = connection_info
return True
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index e33ccef3c..bd2f51e69 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -673,7 +673,7 @@ class LibvirtDriver(driver.ComputeDriver):
def volume_driver_method(self, method_name, connection_info,
*args, **kwargs):
driver_type = connection_info.get('driver_volume_type')
- if not driver_type in self.volume_drivers:
+ if driver_type not in self.volume_drivers:
raise exception.VolumeDriverNotFound(driver_type=driver_type)
driver = self.volume_drivers[driver_type]
method = getattr(driver, method_name)
diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py
index 3323b8f1d..5b712cf42 100644
--- a/nova/virt/libvirt/firewall.py
+++ b/nova/virt/libvirt/firewall.py
@@ -29,11 +29,7 @@ LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('use_ipv6', 'nova.netconf')
-try:
- import libvirt
-except ImportError:
- LOG.warn(_("Libvirt module could not be loaded. NWFilterFirewall will "
- "not work correctly."))
+libvirt = None
class NWFilterFirewall(base_firewall.FirewallDriver):
@@ -47,6 +43,13 @@ class NWFilterFirewall(base_firewall.FirewallDriver):
def __init__(self, virtapi, get_connection, **kwargs):
super(NWFilterFirewall, self).__init__(virtapi)
+ global libvirt
+ if libvirt is None:
+ try:
+ libvirt = __import__('libvirt')
+ except ImportError:
+ LOG.warn(_("Libvirt module could not be loaded. "
+ "NWFilterFirewall will not work correctly."))
self._libvirt_get_connection = get_connection
self.static_filters_configured = False
self.handle_security_groups = False
diff --git a/nova/virt/libvirt/imagecache.py b/nova/virt/libvirt/imagecache.py
index 8f677b482..ea7bded95 100644
--- a/nova/virt/libvirt/imagecache.py
+++ b/nova/virt/libvirt/imagecache.py
@@ -305,7 +305,7 @@ class ImageCacheManager(object):
backing_path = os.path.join(CONF.instances_path,
CONF.base_dir_name,
backing_file)
- if not backing_path in inuse_images:
+ if backing_path not in inuse_images:
inuse_images.append(backing_path)
if backing_path in self.unexplained_images:
@@ -464,7 +464,7 @@ class ImageCacheManager(object):
# _verify_checksum returns True if the checksum is ok, and None if
# there is no checksum file
checksum_result = self._verify_checksum(img_id, base_file)
- if not checksum_result is None:
+ if checksum_result is not None:
image_bad = not checksum_result
# Give other threads a chance to run
@@ -555,7 +555,7 @@ class ImageCacheManager(object):
# Elements remaining in unexplained_images might be in use
inuse_backing_images = self._list_backing_images()
for backing_path in inuse_backing_images:
- if not backing_path in self.active_base_files:
+ if backing_path not in self.active_base_files:
self.active_base_files.append(backing_path)
# Anything left is an unknown base image
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index bd4ec685c..88110055c 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -275,7 +275,7 @@ def pick_disk_driver_name(is_block_dev=False):
if is_block_dev:
return "phy"
else:
- return "file"
+ return "tap"
elif CONF.libvirt_type in ('kvm', 'qemu'):
return "qemu"
else:
diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py
index f9a948fb5..d02db22f3 100644
--- a/nova/virt/libvirt/volume.py
+++ b/nova/virt/libvirt/volume.py
@@ -47,19 +47,19 @@ CONF = cfg.CONF
CONF.register_opts(volume_opts)
-class LibvirtVolumeDriver(object):
+class LibvirtBaseVolumeDriver(object):
"""Base class for volume drivers."""
- def __init__(self, connection):
+ def __init__(self, connection, is_block_dev):
self.connection = connection
+ self.is_block_dev = is_block_dev
def connect_volume(self, connection_info, mount_device):
"""Connect the volume. Returns xml for libvirt."""
+
conf = vconfig.LibvirtConfigGuestDisk()
- conf.source_type = "block"
- conf.driver_name = virtutils.pick_disk_driver_name(is_block_dev=True)
+ conf.driver_name = virtutils.pick_disk_driver_name(self.is_block_dev)
conf.driver_format = "raw"
conf.driver_cache = "none"
- conf.source_path = connection_info['data']['device_path']
conf.target_dev = mount_device
conf.target_bus = "virtio"
conf.serial = connection_info.get('serial')
@@ -70,37 +70,49 @@ class LibvirtVolumeDriver(object):
pass
-class LibvirtFakeVolumeDriver(LibvirtVolumeDriver):
- """Driver to attach Network volumes to libvirt."""
+class LibvirtVolumeDriver(LibvirtBaseVolumeDriver):
+ """Class for volumes backed by local file."""
+ def __init__(self, connection):
+ super(LibvirtVolumeDriver,
+ self).__init__(connection, is_block_dev=True)
def connect_volume(self, connection_info, mount_device):
- conf = vconfig.LibvirtConfigGuestDisk()
+ """Connect the volume to a local device."""
+ conf = super(LibvirtVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
+ conf.source_type = "block"
+ conf.source_path = connection_info['data']['device_path']
+ return conf
+
+
+class LibvirtFakeVolumeDriver(LibvirtBaseVolumeDriver):
+ """Driver to attach fake volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtFakeVolumeDriver,
+ self).__init__(connection, is_block_dev=True)
+
+ def connect_volume(self, connection_info, mount_device):
+ """Connect the volume to a fake device."""
+ conf = super(LibvirtFakeVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
conf.source_type = "network"
- conf.driver_name = "qemu"
- conf.driver_format = "raw"
- conf.driver_cache = "none"
conf.source_protocol = "fake"
conf.source_host = "fake"
- conf.target_dev = mount_device
- conf.target_bus = "virtio"
- conf.serial = connection_info.get('serial')
return conf
-class LibvirtNetVolumeDriver(LibvirtVolumeDriver):
+class LibvirtNetVolumeDriver(LibvirtBaseVolumeDriver):
"""Driver to attach Network volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtNetVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def connect_volume(self, connection_info, mount_device):
- conf = vconfig.LibvirtConfigGuestDisk()
+ conf = super(LibvirtNetVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
conf.source_type = "network"
- conf.driver_name = virtutils.pick_disk_driver_name(is_block_dev=False)
- conf.driver_format = "raw"
- conf.driver_cache = "none"
conf.source_protocol = connection_info['driver_volume_type']
conf.source_host = connection_info['data']['name']
- conf.target_dev = mount_device
- conf.target_bus = "virtio"
- conf.serial = connection_info.get('serial')
netdisk_properties = connection_info['data']
auth_enabled = netdisk_properties.get('auth_enabled')
if (conf.source_protocol == 'rbd' and
@@ -118,8 +130,11 @@ class LibvirtNetVolumeDriver(LibvirtVolumeDriver):
return conf
-class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
+class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
"""Driver to attach Network volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtISCSIVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def _run_iscsiadm(self, iscsi_properties, iscsi_command, **kwargs):
check_exit_code = kwargs.pop('check_exit_code', 0)
@@ -141,6 +156,9 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
@lockutils.synchronized('connect_volume', 'nova-')
def connect_volume(self, connection_info, mount_device):
"""Attach the volume to instance_name."""
+ conf = super(LibvirtISCSIVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
+
iscsi_properties = connection_info['data']
# NOTE(vish): If we are on the same host as nova volume, the
# discovery makes the target so we don't need to
@@ -204,15 +222,15 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
"(after %(tries)s rescans)") %
locals())
- connection_info['data']['device_path'] = host_device
- sup = super(LibvirtISCSIVolumeDriver, self)
- return sup.connect_volume(connection_info, mount_device)
+ conf.source_type = "block"
+ conf.source_path = host_device
+ return conf
@lockutils.synchronized('connect_volume', 'nova-')
def disconnect_volume(self, connection_info, mount_device):
"""Detach the volume from instance_name."""
- sup = super(LibvirtISCSIVolumeDriver, self)
- sup.disconnect_volume(connection_info, mount_device)
+ super(LibvirtISCSIVolumeDriver,
+ self).disconnect_volume(connection_info, mount_device)
iscsi_properties = connection_info['data']
# NOTE(vish): Only disconnect from the target if no luns from the
# target are in use.
diff --git a/nova/virt/libvirt/volume_nfs.py b/nova/virt/libvirt/volume_nfs.py
index b5083937d..70bb8c38f 100644
--- a/nova/virt/libvirt/volume_nfs.py
+++ b/nova/virt/libvirt/volume_nfs.py
@@ -38,27 +38,24 @@ CONF = cfg.CONF
CONF.register_opts(volume_opts)
-class NfsVolumeDriver(volume.LibvirtVolumeDriver):
+class NfsVolumeDriver(volume.LibvirtBaseVolumeDriver):
"""Class implements libvirt part of volume driver for NFS."""
- def __init__(self, *args, **kwargs):
- """Create back-end to nfs and check connection."""
- super(NfsVolumeDriver, self).__init__(*args, **kwargs)
+ def __init__(self, connection):
+ """Create back-end to nfs."""
+ super(NfsVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def connect_volume(self, connection_info, mount_device):
"""Connect the volume. Returns xml for libvirt."""
+ conf = super(NfsVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
path = self._ensure_mounted(connection_info['data']['export'])
path = os.path.join(path, connection_info['data']['name'])
- connection_info['data']['device_path'] = path
- conf = super(NfsVolumeDriver, self).connect_volume(connection_info,
- mount_device)
conf.source_type = 'file'
+ conf.source_path = path
return conf
- def disconnect_volume(self, connection_info, mount_device):
- """Disconnect the volume."""
- pass
-
def _ensure_mounted(self, nfs_export):
"""
@type nfs_export: string
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index 666e46754..bdadfbc38 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -89,7 +89,7 @@ def reset():
def reset_table(table):
- if not table in _CLASSES:
+ if table not in _CLASSES:
return
_db_content[table] = {}
@@ -417,7 +417,7 @@ class SessionBase(object):
def VBD_add_to_other_config(self, _1, vbd_ref, key, value):
db_ref = _db_content['VBD'][vbd_ref]
- if not 'other_config' in db_ref:
+ if 'other_config' not in db_ref:
db_ref['other_config'] = {}
if key in db_ref['other_config']:
raise Failure(['MAP_DUPLICATE_KEY', 'VBD', 'other_config',
@@ -426,7 +426,7 @@ class SessionBase(object):
def VBD_get_other_config(self, _1, vbd_ref):
db_ref = _db_content['VBD'][vbd_ref]
- if not 'other_config' in db_ref:
+ if 'other_config' not in db_ref:
return {}
return db_ref['other_config']
@@ -497,14 +497,14 @@ class SessionBase(object):
def VM_remove_from_xenstore_data(self, _1, vm_ref, key):
db_ref = _db_content['VM'][vm_ref]
- if not 'xenstore_data' in db_ref:
+ if 'xenstore_data' not in db_ref:
return
if key in db_ref['xenstore_data']:
del db_ref['xenstore_data'][key]
def VM_add_to_xenstore_data(self, _1, vm_ref, key, value):
db_ref = _db_content['VM'][vm_ref]
- if not 'xenstore_data' in db_ref:
+ if 'xenstore_data' not in db_ref:
db_ref['xenstore_data'] = {}
db_ref['xenstore_data'][key] = value
@@ -513,14 +513,14 @@ class SessionBase(object):
def VDI_remove_from_other_config(self, _1, vdi_ref, key):
db_ref = _db_content['VDI'][vdi_ref]
- if not 'other_config' in db_ref:
+ if 'other_config' not in db_ref:
return
if key in db_ref['other_config']:
del db_ref['other_config'][key]
def VDI_add_to_other_config(self, _1, vdi_ref, key, value):
db_ref = _db_content['VDI'][vdi_ref]
- if not 'other_config' in db_ref:
+ if 'other_config' not in db_ref:
db_ref['other_config'] = {}
if key in db_ref['other_config']:
raise Failure(['MAP_DUPLICATE_KEY', 'VDI', 'other_config',
diff --git a/nova/virt/xenapi/imageupload/__init__.py b/nova/virt/xenapi/imageupload/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/virt/xenapi/imageupload/__init__.py
diff --git a/nova/virt/xenapi/imageupload/glance.py b/nova/virt/xenapi/imageupload/glance.py
new file mode 100644
index 000000000..adc06f65b
--- /dev/null
+++ b/nova/virt/xenapi/imageupload/glance.py
@@ -0,0 +1,54 @@
+# Copyright 2013 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.
+
+from nova.image import glance
+from nova.openstack.common import cfg
+import nova.openstack.common.log as logging
+from nova.virt.xenapi import vm_utils
+
+LOG = logging.getLogger(__name__)
+
+CONF = cfg.CONF
+
+
+class GlanceStore(object):
+
+ def upload_image(self, context, session, instance, vdi_uuids, image_id):
+ """Requests that the Glance plugin bundle the specified VDIs and
+ push them into Glance using the specified human-friendly name.
+ """
+ # NOTE(sirp): Currently we only support uploading images as VHD, there
+ # is no RAW equivalent (yet)
+ LOG.debug(_("Asking xapi to upload to glance %(vdi_uuids)s as"
+ " ID %(image_id)s"), locals(), instance=instance)
+
+ glance_api_servers = glance.get_api_servers()
+ glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
+
+ properties = {
+ 'auto_disk_config': instance['auto_disk_config'],
+ 'os_type': instance['os_type'] or CONF.default_os_type,
+ }
+
+ params = {'vdi_uuids': vdi_uuids,
+ 'image_id': image_id,
+ 'glance_host': glance_host,
+ 'glance_port': glance_port,
+ 'glance_use_ssl': glance_use_ssl,
+ 'sr_path': vm_utils.get_sr_path(session),
+ 'auth_token': getattr(context, 'auth_token', None),
+ 'properties': properties}
+
+ session.call_plugin_serialized('glance', 'upload_vhd', **params)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 3972cf765..ec6450d9f 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -714,35 +714,6 @@ def _find_cached_image(session, image_id, sr_ref):
return cached_images.get(image_id)
-def upload_image(context, session, instance, vdi_uuids, image_id):
- """Requests that the Glance plugin bundle the specified VDIs and
- push them into Glance using the specified human-friendly name.
- """
- # NOTE(sirp): Currently we only support uploading images as VHD, there
- # is no RAW equivalent (yet)
- LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as"
- " ID %(image_id)s"), locals(), instance=instance)
-
- glance_api_servers = glance.get_api_servers()
- glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
-
- properties = {
- 'auto_disk_config': instance['auto_disk_config'],
- 'os_type': instance['os_type'] or CONF.default_os_type,
- }
-
- params = {'vdi_uuids': vdi_uuids,
- 'image_id': image_id,
- 'glance_host': glance_host,
- 'glance_port': glance_port,
- 'glance_use_ssl': glance_use_ssl,
- 'sr_path': get_sr_path(session),
- 'auth_token': getattr(context, 'auth_token', None),
- 'properties': properties}
-
- session.call_plugin_serialized('glance', 'upload_vhd', **params)
-
-
def resize_disk(session, instance, vdi_ref, instance_type):
# Copy VDI over to something we can resize
# NOTE(jerdfelt): Would be nice to just set vdi_ref to read/write
@@ -1588,7 +1559,7 @@ def _find_iso_sr(session):
if not sr_rec['content_type'] == 'iso':
LOG.debug(_("ISO: not iso content"))
continue
- if not 'i18n-key' in sr_rec['other_config']:
+ if 'i18n-key' not in sr_rec['other_config']:
LOG.debug(_("ISO: iso content_type, no 'i18n-key' key"))
continue
if not sr_rec['other_config']['i18n-key'] == 'local-storage-iso':
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index cd3a2f2f0..8a76f3368 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -60,7 +60,10 @@ xenapi_vmops_opts = [
'to go to running state'),
cfg.StrOpt('xenapi_vif_driver',
default='nova.virt.xenapi.vif.XenAPIBridgeDriver',
- help='The XenAPI VIF driver using XenServer Network APIs.')
+ help='The XenAPI VIF driver using XenServer Network APIs.'),
+ cfg.StrOpt('xenapi_image_upload_handler',
+ default='nova.virt.xenapi.imageupload.glance.GlanceStore',
+ help='Object Store Driver used to handle image uploads.'),
]
CONF = cfg.CONF
@@ -162,6 +165,11 @@ class VMOps(object):
self.vif_driver = vif_impl(xenapi_session=self._session)
self.default_root_dev = '/dev/sda'
+ msg = _("Importing image upload handler: %s")
+ LOG.debug(msg % CONF.xenapi_image_upload_handler)
+ self.image_upload_handler = importutils.import_object(
+ CONF.xenapi_image_upload_handler)
+
@property
def agent_enabled(self):
return not CONF.xenapi_disable_agent
@@ -676,9 +684,11 @@ class VMOps(object):
coalesce together, so, we must wait for this coalescing to occur to
get a stable representation of the data on disk.
- 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer
- that will bundle the VHDs together and then push the bundle into
- Glance.
+ 3. Push-to-data-store: Once coalesced, we call a plugin on the
+ XenServer that will bundle the VHDs together and then push the
+ bundle. Depending on the configured value of
+ 'xenapi_image_upload_handler', image data may be pushed to
+ Glance or the specified data store.
"""
vm_ref = self._get_vm_opaque_ref(instance)
@@ -689,8 +699,11 @@ class VMOps(object):
update_task_state) as vdi_uuids:
update_task_state(task_state=task_states.IMAGE_UPLOADING,
expected_state=task_states.IMAGE_PENDING_UPLOAD)
- vm_utils.upload_image(
- context, self._session, instance, vdi_uuids, image_id)
+ self.image_upload_handler.upload_image(context,
+ self._session,
+ instance,
+ vdi_uuids,
+ image_id)
LOG.debug(_("Finished snapshot and upload for VM"),
instance=instance)
diff --git a/openstack-common.conf b/openstack-common.conf
index a0b14e651..f9d38ea1d 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,cliutils,context,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,iniparser,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils
+modules=cfg,cliutils,context,excutils,eventlet_backdoor,fileutils,gettextutils,importutils,iniparser,jsonutils,local,lockutils,log,network_utils,notifier,plugin,policy,rootwrap,setup,timeutils,rpc,uuidutils,install_venv_common
# The base module to hold the copy of openstack.common
base=nova
diff --git a/tools/install_venv.py b/tools/install_venv.py
index b1ceb74f0..abf56ea0e 100644
--- a/tools/install_venv.py
+++ b/tools/install_venv.py
@@ -1,4 +1,3 @@
-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
@@ -6,6 +5,7 @@
# All Rights Reserved.
#
# Copyright 2010 OpenStack, LLC
+# Copyright 2013 IBM Corp.
#
# 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
@@ -19,186 +19,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-"""Installation script for Nova's development virtualenv."""
-
-import optparse
import os
-import subprocess
import sys
-
-ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-VENV = os.path.join(ROOT, '.venv')
-PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
-TEST_REQUIRES = os.path.join(ROOT, 'tools', 'test-requires')
-PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
-
-
-def die(message, *args):
- print >> sys.stderr, message % args
- sys.exit(1)
-
-
-def check_python_version():
- if sys.version_info < (2, 6):
- die("Need Python Version >= 2.6")
-
-
-def run_command_with_code(cmd, redirect_output=True, check_exit_code=True):
- """Runs a command in an out-of-process shell.
-
- Returns the output of that command. Working directory is ROOT.
- """
- if redirect_output:
- stdout = subprocess.PIPE
- else:
- stdout = None
-
- proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
- output = proc.communicate()[0]
- if check_exit_code and proc.returncode != 0:
- die('Command "%s" failed.\n%s', ' '.join(cmd), output)
- return (output, proc.returncode)
-
-
-def run_command(cmd, redirect_output=True, check_exit_code=True):
- return run_command_with_code(cmd, redirect_output, check_exit_code)[0]
-
-
-class Distro(object):
-
- def check_cmd(self, cmd):
- return bool(run_command(['which', cmd], check_exit_code=False).strip())
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if self.check_cmd('easy_install'):
- print 'Installing virtualenv via easy_install...',
- if run_command(['easy_install', 'virtualenv']):
- print 'Succeeded'
- return
- else:
- print 'Failed'
-
- die('ERROR: virtualenv not found.\n\nNova development'
- ' requires virtualenv, please install it using your'
- ' favorite package management tool')
-
- def post_process(self):
- """Any distribution-specific post-processing gets done here.
-
- In particular, this is useful for applying patches to code inside
- the venv.
- """
- pass
-
-
-class Fedora(Distro):
- """This covers all Fedora-based distributions.
-
- Includes: Fedora, RHEL, CentOS, Scientific Linux"""
-
- def check_pkg(self, pkg):
- return run_command_with_code(['rpm', '-q', pkg],
- check_exit_code=False)[1] == 0
-
- def yum_install(self, pkg, **kwargs):
- print "Attempting to install '%s' via yum" % pkg
- run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
-
- def apply_patch(self, originalfile, patchfile):
- run_command(['patch', originalfile, patchfile])
-
- def install_virtualenv(self):
- if self.check_cmd('virtualenv'):
- return
-
- if not self.check_pkg('python-virtualenv'):
- self.yum_install('python-virtualenv', check_exit_code=False)
-
- super(Fedora, self).install_virtualenv()
-
- def post_process(self):
- """Workaround for a bug in eventlet.
-
- This currently affects RHEL6.1, but the fix can safely be
- applied to all RHEL and Fedora distributions.
-
- This can be removed when the fix is applied upstream.
-
- Nova: https://bugs.launchpad.net/nova/+bug/884915
- Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
- """
-
- # Install "patch" program if it's not there
- if not self.check_pkg('patch'):
- self.yum_install('patch')
-
- # Apply the eventlet patch
- self.apply_patch(os.path.join(VENV, 'lib', PY_VERSION, 'site-packages',
- 'eventlet/green/subprocess.py'),
- 'contrib/redhat-eventlet.patch')
-
-
-def get_distro():
- if (os.path.exists('/etc/fedora-release') or
- os.path.exists('/etc/redhat-release')):
- return Fedora()
- else:
- return Distro()
-
-
-def check_dependencies():
- get_distro().install_virtualenv()
-
-
-def create_virtualenv(venv=VENV, no_site_packages=True):
- """Creates the virtual environment and installs PIP.
-
- Creates the virtual environment and installs PIP only into the
- virtual environment.
- """
- print 'Creating venv...',
- if no_site_packages:
- run_command(['virtualenv', '-q', '--no-site-packages', VENV])
- else:
- run_command(['virtualenv', '-q', VENV])
- print 'done.'
- print 'Installing pip in virtualenv...',
- if not run_command(['tools/with_venv.sh', 'easy_install',
- 'pip>1.0']).strip():
- die("Failed to install pip.")
- print 'done.'
-
-
-def pip_install(*args):
- run_command(['tools/with_venv.sh',
- 'pip', 'install', '--upgrade'] + list(args),
- redirect_output=False)
-
-
-def install_dependencies(venv=VENV):
- print 'Installing dependencies with pip (this can take a while)...'
-
- # First things first, make sure our venv has the latest pip and distribute.
- # NOTE: we keep pip at version 1.1 since the most recent version causes
- # the .venv creation to fail. See:
- # https://bugs.launchpad.net/nova/+bug/1047120
- pip_install('pip==1.1')
- pip_install('distribute')
-
- # Install greenlet by hand - just listing it in the requires file does not
- # get it in stalled in the right order
- pip_install('greenlet')
-
- pip_install('-r', PIP_REQUIRES)
- pip_install('-r', TEST_REQUIRES)
-
-
-def post_process():
- get_distro().post_process()
+import install_venv_common as install_venv
def print_help():
@@ -223,22 +47,21 @@ def print_help():
print help
-def parse_args():
- """Parses command-line arguments."""
- parser = optparse.OptionParser()
- parser.add_option("-n", "--no-site-packages", dest="no_site_packages",
- default=False, action="store_true",
- help="Do not inherit packages from global Python install")
- return parser.parse_args()
-
-
def main(argv):
- (options, args) = parse_args()
- check_python_version()
- check_dependencies()
- create_virtualenv(no_site_packages=options.no_site_packages)
- install_dependencies()
- post_process()
+ root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+ venv = os.path.join(root, '.venv')
+ pip_requires = os.path.join(root, 'tools', 'pip-requires')
+ test_requires = os.path.join(root, 'tools', 'test-requires')
+ py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
+ project = 'Nova'
+ install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
+ py_version, project)
+ options = install.parse_args(argv)
+ install.check_python_version()
+ install.check_dependencies()
+ install.create_virtualenv(no_site_packages=options.no_site_packages)
+ install.install_dependencies()
+ install.post_process()
print_help()
if __name__ == '__main__':
diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py
new file mode 100644
index 000000000..b15011a00
--- /dev/null
+++ b/tools/install_venv_common.py
@@ -0,0 +1,225 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack, LLC
+# Copyright 2013 IBM Corp.
+#
+# 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.
+
+"""Provides methods needed by installation script for OpenStack development
+virtual environments.
+
+Synced in from openstack-common
+"""
+
+import os
+import subprocess
+import sys
+
+from nova.openstack.common import cfg
+
+
+class InstallVenv(object):
+
+ def __init__(self, root, venv, pip_requires, test_requires, py_version,
+ project):
+ self.root = root
+ self.venv = venv
+ self.pip_requires = pip_requires
+ self.test_requires = test_requires
+ self.py_version = py_version
+ self.project = project
+
+ def die(self, message, *args):
+ print >> sys.stderr, message % args
+ sys.exit(1)
+
+ def check_python_version(self):
+ if sys.version_info < (2, 6):
+ self.die("Need Python Version >= 2.6")
+
+ def run_command_with_code(self, cmd, redirect_output=True,
+ check_exit_code=True):
+ """Runs a command in an out-of-process shell.
+
+ Returns the output of that command. Working directory is ROOT.
+ """
+ if redirect_output:
+ stdout = subprocess.PIPE
+ else:
+ stdout = None
+
+ proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
+ output = proc.communicate()[0]
+ if check_exit_code and proc.returncode != 0:
+ self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
+ return (output, proc.returncode)
+
+ def run_command(self, cmd, redirect_output=True, check_exit_code=True):
+ return self.run_command_with_code(cmd, redirect_output,
+ check_exit_code)[0]
+
+ def get_distro(self):
+ if (os.path.exists('/etc/fedora-release') or
+ os.path.exists('/etc/redhat-release')):
+ return Fedora(self.root, self.venv, self.pip_requires,
+ self.test_requires, self.py_version, self.project)
+ else:
+ return Distro(self.root, self.venv, self.pip_requires,
+ self.test_requires, self.py_version, self.project)
+
+ def check_dependencies(self):
+ self.get_distro().install_virtualenv()
+
+ def create_virtualenv(self, no_site_packages=True):
+ """Creates the virtual environment and installs PIP.
+
+ Creates the virtual environment and installs PIP only into the
+ virtual environment.
+ """
+ if not os.path.isdir(self.venv):
+ print 'Creating venv...',
+ if no_site_packages:
+ self.run_command(['virtualenv', '-q', '--no-site-packages',
+ self.venv])
+ else:
+ self.run_command(['virtualenv', '-q', self.venv])
+ print 'done.'
+ print 'Installing pip in virtualenv...',
+ if not self.run_command(['tools/with_venv.sh', 'easy_install',
+ 'pip>1.0']).strip():
+ self.die("Failed to install pip.")
+ print 'done.'
+ else:
+ print "venv already exists..."
+ pass
+
+ def pip_install(self, *args):
+ self.run_command(['tools/with_venv.sh',
+ 'pip', 'install', '--upgrade'] + list(args),
+ redirect_output=False)
+
+ def install_dependencies(self):
+ print 'Installing dependencies with pip (this can take a while)...'
+
+ # First things first, make sure our venv has the latest pip and
+ # distribute.
+ # NOTE: we keep pip at version 1.1 since the most recent version causes
+ # the .venv creation to fail. See:
+ # https://bugs.launchpad.net/nova/+bug/1047120
+ self.pip_install('pip==1.1')
+ self.pip_install('distribute')
+
+ # Install greenlet by hand - just listing it in the requires file does
+ # not
+ # get it installed in the right order
+ self.pip_install('greenlet')
+
+ self.pip_install('-r', self.pip_requires)
+ self.pip_install('-r', self.test_requires)
+
+ def post_process(self):
+ self.get_distro().post_process()
+
+ def parse_args(self, argv):
+ """Parses command-line arguments."""
+ cli_opts = [
+ cfg.BoolOpt('no-site-packages',
+ default=False,
+ short='n',
+ help="Do not inherit packages from global Python"
+ "install"),
+ ]
+ CLI = cfg.ConfigOpts()
+ CLI.register_cli_opts(cli_opts)
+ CLI(argv[1:])
+ return CLI
+
+
+class Distro(InstallVenv):
+
+ def check_cmd(self, cmd):
+ return bool(self.run_command(['which', cmd],
+ check_exit_code=False).strip())
+
+ def install_virtualenv(self):
+ if self.check_cmd('virtualenv'):
+ return
+
+ if self.check_cmd('easy_install'):
+ print 'Installing virtualenv via easy_install...',
+ if self.run_command(['easy_install', 'virtualenv']):
+ print 'Succeeded'
+ return
+ else:
+ print 'Failed'
+
+ self.die('ERROR: virtualenv not found.\n\n%s development'
+ ' requires virtualenv, please install it using your'
+ ' favorite package management tool' % self.project)
+
+ def post_process(self):
+ """Any distribution-specific post-processing gets done here.
+
+ In particular, this is useful for applying patches to code inside
+ the venv.
+ """
+ pass
+
+
+class Fedora(Distro):
+ """This covers all Fedora-based distributions.
+
+ Includes: Fedora, RHEL, CentOS, Scientific Linux
+ """
+
+ def check_pkg(self, pkg):
+ return self.run_command_with_code(['rpm', '-q', pkg],
+ check_exit_code=False)[1] == 0
+
+ def yum_install(self, pkg, **kwargs):
+ print "Attempting to install '%s' via yum" % pkg
+ self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs)
+
+ def apply_patch(self, originalfile, patchfile):
+ self.run_command(['patch', originalfile, patchfile])
+
+ def install_virtualenv(self):
+ if self.check_cmd('virtualenv'):
+ return
+
+ if not self.check_pkg('python-virtualenv'):
+ self.yum_install('python-virtualenv', check_exit_code=False)
+
+ super(Fedora, self).install_virtualenv()
+
+ def post_process(self):
+ """Workaround for a bug in eventlet.
+
+ This currently affects RHEL6.1, but the fix can safely be
+ applied to all RHEL and Fedora distributions.
+
+ This can be removed when the fix is applied upstream.
+
+ Nova: https://bugs.launchpad.net/nova/+bug/884915
+ Upstream: https://bitbucket.org/which_linden/eventlet/issue/89
+ """
+
+ # Install "patch" program if it's not there
+ if not self.check_pkg('patch'):
+ self.yum_install('patch')
+
+ # Apply the eventlet patch
+ self.apply_patch(os.path.join(self.venv, 'lib', self.py_version,
+ 'site-packages',
+ 'eventlet/green/subprocess.py'),
+ 'contrib/redhat-eventlet.patch')