From e0105efd2e7f17d1c09f4653fa898a2aae6f39f8 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 16:39:04 -0700 Subject: [PATCH 1/9] Set XML namespace when returning XML --- nova/wsgi.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/wsgi.py b/nova/wsgi.py index ba0819466..f0a60582d 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -478,6 +478,9 @@ class Serializer(object): root_key = data.keys()[0] doc = minidom.Document() node = self._to_xml_node(doc, metadata, root_key, data[root_key]) + node.setAttribute('xmlns', + 'http://docs.rackspacecloud.com/servers/api/v1.0') + return node.toprettyxml(indent=' ') def _to_xml_node(self, doc, metadata, nodename, data): From 49d22a4efb84dd1712a98b2d64838d270e74f6ef Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 16:59:03 -0700 Subject: [PATCH 2/9] Support setting the xmlns intelligently --- nova/wsgi.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/nova/wsgi.py b/nova/wsgi.py index f0a60582d..5d286bb3b 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -355,7 +355,8 @@ class Controller(object): if type(result) is dict: content_type = req.best_match_content_type() - body = self._serialize(result, content_type) + default_xmlns = self.get_default_xmlns(req) + body = self._serialize(result, content_type, default_xmlns) response = webob.Response() response.headers["Content-Type"] = content_type @@ -365,14 +366,15 @@ class Controller(object): else: return result - def _serialize(self, data, content_type): + def _serialize(self, data, content_type, default_xmlns): """ Serialize the given dict to the provided content_type. Uses self._serialization_metadata if it exists, which is a dict mapping MIME types to information needed to serialize to that type. """ _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(_metadata) + + serializer = Serializer(_metadata, default_xmlns) try: return serializer.serialize(data, content_type) except exception.InvalidContentType: @@ -388,19 +390,23 @@ class Controller(object): serializer = Serializer(_metadata) return serializer.deserialize(data, content_type) + def get_default_xmlns(self, req): + return 'http://docs.rackspacecloud.com/servers/api/v1.0' + class Serializer(object): """ Serializes and deserializes dictionaries to certain MIME types. """ - def __init__(self, metadata=None): + def __init__(self, metadata=None, default_xmlns=None): """ Create a serializer based on the given WSGI environment. 'metadata' is an optional dict mapping MIME types to information needed to serialize a dictionary to that type. """ self.metadata = metadata or {} + self.default_xmlns = default_xmlns def _get_serialize_handler(self, content_type): handlers = { @@ -478,14 +484,23 @@ class Serializer(object): root_key = data.keys()[0] doc = minidom.Document() node = self._to_xml_node(doc, metadata, root_key, data[root_key]) - node.setAttribute('xmlns', - 'http://docs.rackspacecloud.com/servers/api/v1.0') + + xmlns = node.getAttribute('xmlns') + if not xmlns and self.default_xmlns: + node.setAttribute('xmlns', self.default_xmlns) return node.toprettyxml(indent=' ') def _to_xml_node(self, doc, metadata, nodename, data): """Recursive method to convert data members to XML nodes.""" result = doc.createElement(nodename) + + # Set the xml namespace if one is specified + # TODO(justinsb): We could also use prefixes on the keys + xmlns = metadata.get('xmlns', None) + if xmlns: + result.setAttribute('xmlns', xmlns) + if type(data) is list: singular = metadata.get('plurals', {}).get(nodename, None) if singular is None: From f21d2eb71f6f909fc3f01c0b1f6f518ba1458dd9 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Tue, 22 Mar 2011 21:18:31 -0700 Subject: [PATCH 3/9] Added note agreeing with Brian Lamar that the namespace doesn't belong in wsgi --- nova/wsgi.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nova/wsgi.py b/nova/wsgi.py index 5d286bb3b..1bcc08f7f 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -391,6 +391,10 @@ class Controller(object): return serializer.deserialize(data, content_type) def get_default_xmlns(self, req): + # NOTE(justinsb): This doesn't really belong here.. + # We'll probably end up moving this into a new OpenstackApiController + # class or something like that, once we know what's going to happen + # with v1.1 return 'http://docs.rackspacecloud.com/servers/api/v1.0' From b933ca56c22b2df17c3acdeeeb750c5581717983 Mon Sep 17 00:00:00 2001 From: John Tran Date: Tue, 29 Mar 2011 13:44:38 -0700 Subject: [PATCH 4/9] initial unit test for describe images --- nova/tests/test_cloud.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 00803d0ad..791498f89 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -216,6 +216,16 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp1['id']) db.service_destroy(self.context, comp2['id']) + def test_describe_images(self): + def fake_detail(meh, context): + return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type':'machine'}}] + + self.stubs.Set(local.LocalImageService, 'detail', fake_detail) + result = self.cloud.describe_images(self.context) + result = result['imagesSet'][0] + self.assertEqual(result['imageId'], 'ami-00000001') + def test_console_output(self): instance_type = FLAGS.default_instance_type max_count = 1 From 502bec8597509db87e033db83094388d335b45f6 Mon Sep 17 00:00:00 2001 From: John Tran Date: Tue, 29 Mar 2011 17:07:59 -0700 Subject: [PATCH 5/9] adding unit tests for describe_images --- Authors | 1 + nova/tests/test_cloud.py | 31 ++++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Authors b/Authors index eccf38a43..48b912184 100644 --- a/Authors +++ b/Authors @@ -32,6 +32,7 @@ Jesse Andrews Joe Heck Joel Moore John Dewey +John Tran Jonathan Bryce Jordan Rinke Josh Durgin diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py index 791498f89..5cb969979 100644 --- a/nova/tests/test_cloud.py +++ b/nova/tests/test_cloud.py @@ -41,6 +41,7 @@ from nova.compute import power_state from nova.api.ec2 import cloud from nova.api.ec2 import ec2utils from nova.image import local +from nova.exception import NotFound FLAGS = flags.FLAGS @@ -71,7 +72,8 @@ class CloudTestCase(test.TestCase): host = self.network.get_network_host(self.context.elevated()) def fake_show(meh, context, id): - return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1}} + return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, + 'type': 'machine'}} self.stubs.Set(local.LocalImageService, 'show', fake_show) self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show) @@ -217,14 +219,33 @@ class CloudTestCase(test.TestCase): db.service_destroy(self.context, comp2['id']) def test_describe_images(self): + describe_images = self.cloud.describe_images + def fake_detail(meh, context): return [{'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1, - 'type':'machine'}}] + 'type': 'machine'}}] + + def fake_show_none(meh, context, id): + raise NotFound self.stubs.Set(local.LocalImageService, 'detail', fake_detail) - result = self.cloud.describe_images(self.context) - result = result['imagesSet'][0] - self.assertEqual(result['imageId'], 'ami-00000001') + # list all + result1 = describe_images(self.context) + result1 = result1['imagesSet'][0] + self.assertEqual(result1['imageId'], 'ami-00000001') + # provided a valid image_id + result2 = describe_images(self.context, ['ami-00000001']) + self.assertEqual(1, len(result2['imagesSet'])) + # provide more than 1 valid image_id + result3 = describe_images(self.context, ['ami-00000001', + 'ami-00000002']) + self.assertEqual(2, len(result3['imagesSet'])) + # provide an non-existing image_id + self.stubs.UnsetAll() + self.stubs.Set(local.LocalImageService, 'show', fake_show_none) + self.stubs.Set(local.LocalImageService, 'show_by_name', fake_show_none) + self.assertRaises(NotFound, describe_images, + self.context, ['ami-fake']) def test_console_output(self): instance_type = FLAGS.default_instance_type From 0356f63aa00502b26517c82ba76fcc5dfda08847 Mon Sep 17 00:00:00 2001 From: Justin Santa Barbara Date: Wed, 30 Mar 2011 10:05:06 -0700 Subject: [PATCH 6/9] Add XML namespaces to the OpenStack API --- nova/wsgi.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/nova/wsgi.py b/nova/wsgi.py index 1bcc08f7f..48502eebb 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -362,7 +362,6 @@ class Controller(object): response.headers["Content-Type"] = content_type response.body = body return response - else: return result @@ -391,11 +390,8 @@ class Controller(object): return serializer.deserialize(data, content_type) def get_default_xmlns(self, req): - # NOTE(justinsb): This doesn't really belong here.. - # We'll probably end up moving this into a new OpenstackApiController - # class or something like that, once we know what's going to happen - # with v1.1 - return 'http://docs.rackspacecloud.com/servers/api/v1.0' + """Provide the XML namespace to use if none is otherwise specified.""" + return None class Serializer(object): From 27e55a300adb1187740f8440906f832630106b06 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 5 Apr 2011 11:42:14 +0200 Subject: [PATCH 7/9] Allow CA code and state to be separated, and make sure CA code gets installed by setup.py install. --- CA/.gitignore | 11 ----- CA/geninter.sh | 39 ----------------- CA/genrootca.sh | 29 ------------- CA/genvpn.sh | 36 ---------------- CA/newcerts/.placeholder | 0 CA/openssl.cnf.tmpl | 90 ---------------------------------------- CA/private/.placeholder | 0 CA/projects/.gitignore | 1 - CA/projects/.placeholder | 0 CA/reqs/.gitignore | 1 - CA/reqs/.placeholder | 0 MANIFEST.in | 2 +- 12 files changed, 1 insertion(+), 208 deletions(-) delete mode 100644 CA/.gitignore delete mode 100755 CA/geninter.sh delete mode 100755 CA/genrootca.sh delete mode 100755 CA/genvpn.sh delete mode 100644 CA/newcerts/.placeholder delete mode 100644 CA/openssl.cnf.tmpl delete mode 100644 CA/private/.placeholder delete mode 100644 CA/projects/.gitignore delete mode 100644 CA/projects/.placeholder delete mode 100644 CA/reqs/.gitignore delete mode 100644 CA/reqs/.placeholder diff --git a/CA/.gitignore b/CA/.gitignore deleted file mode 100644 index fae0922bf..000000000 --- a/CA/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -index.txt -index.txt.old -index.txt.attr -index.txt.attr.old -cacert.pem -serial -serial.old -openssl.cnf -private/* -newcerts/* - diff --git a/CA/geninter.sh b/CA/geninter.sh deleted file mode 100755 index 1fbcc9e73..000000000 --- a/CA/geninter.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# $1 is the id of the project and $2 is the subject of the cert -NAME=$1 -SUBJ=$2 -mkdir -p projects/$NAME -cd projects/$NAME -cp ../../openssl.cnf.tmpl openssl.cnf -sed -i -e s/%USERNAME%/$NAME/g openssl.cnf -mkdir certs crl newcerts private -openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes -echo "10" > serial -touch index.txt -# NOTE(vish): Disabling intermediate ca's because we don't actually need them. -# It makes more sense to have each project have its own root ca. -# openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes -# openssl req -new -sha256 -key private/cakey.pem -out ../../reqs/inter$NAME.csr -batch -subj "$SUBJ" -openssl ca -gencrl -config ./openssl.cnf -out crl.pem -if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then - sudo chown -R nova:nogroup . -fi -# cd ../../ -# openssl ca -extensions v3_ca -days 365 -out INTER/$NAME/cacert.pem -in reqs/inter$NAME.csr -config openssl.cnf -batch diff --git a/CA/genrootca.sh b/CA/genrootca.sh deleted file mode 100755 index 8f2c3ee3f..000000000 --- a/CA/genrootca.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -if [ -f "cacert.pem" ]; -then - echo "Not installing, it's already done." -else - cp openssl.cnf.tmpl openssl.cnf - sed -i -e s/%USERNAME%/ROOT/g openssl.cnf - openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes - touch index.txt - echo "10" > serial - openssl ca -gencrl -config ./openssl.cnf -out crl.pem -fi diff --git a/CA/genvpn.sh b/CA/genvpn.sh deleted file mode 100755 index 7e7db185d..000000000 --- a/CA/genvpn.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# This gets zipped and run on the cloudpipe-managed OpenVPN server -NAME=$1 -SUBJ=$2 - -mkdir -p projects/$NAME -cd projects/$NAME - -# generate a server priv key -openssl genrsa -out server.key 2048 - -# generate a server CSR -openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ" - -novauid=`getent passwd nova | awk -F: '{print $3}'` -if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then - sudo chown -R nova:nogroup . -fi diff --git a/CA/newcerts/.placeholder b/CA/newcerts/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/CA/openssl.cnf.tmpl b/CA/openssl.cnf.tmpl deleted file mode 100644 index dd81f1c2b..000000000 --- a/CA/openssl.cnf.tmpl +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -# -# OpenSSL configuration file. -# - -# Establish working directory. - -dir = . - -[ ca ] -default_ca = CA_default - -[ CA_default ] -serial = $dir/serial -database = $dir/index.txt -new_certs_dir = $dir/newcerts -certificate = $dir/cacert.pem -private_key = $dir/private/cakey.pem -unique_subject = no -default_crl_days = 365 -default_days = 365 -default_md = md5 -preserve = no -email_in_dn = no -nameopt = default_ca -certopt = default_ca -policy = policy_match - -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - - -[ req ] -default_bits = 1024 # Size of keys -default_keyfile = key.pem # name of generated keys -default_md = md5 # message digest algorithm -string_mask = nombstr # permitted characters -distinguished_name = req_distinguished_name - -[ req_distinguished_name ] -# Variable name Prompt string -#---------------------- ---------------------------------- -0.organizationName = Organization Name (company) -organizationalUnitName = Organizational Unit Name (department, division) -emailAddress = Email Address -emailAddress_max = 40 -localityName = Locality Name (city, district) -stateOrProvinceName = State or Province Name (full name) -countryName = Country Name (2 letter code) -countryName_min = 2 -countryName_max = 2 -commonName = Common Name (hostname, IP, or your name) -commonName_max = 64 - -# Default values for the above, for consistency and less typing. -# Variable name Value -#------------------------------ ------------------------------ -0.organizationName_default = NOVA %USERNAME% -localityName_default = Mountain View -stateOrProvinceName_default = California -countryName_default = US - -[ v3_ca ] -basicConstraints = CA:TRUE -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always - -[ v3_req ] -basicConstraints = CA:FALSE -subjectKeyIdentifier = hash diff --git a/CA/private/.placeholder b/CA/private/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/CA/projects/.gitignore b/CA/projects/.gitignore deleted file mode 100644 index 72e8ffc0d..000000000 --- a/CA/projects/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/CA/projects/.placeholder b/CA/projects/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/CA/reqs/.gitignore b/CA/reqs/.gitignore deleted file mode 100644 index 72e8ffc0d..000000000 --- a/CA/reqs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/CA/reqs/.placeholder b/CA/reqs/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/MANIFEST.in b/MANIFEST.in index bf30d1546..e7a6e7da4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,7 @@ include HACKING LICENSE run_tests.py run_tests.sh include README builddeb.sh exercise_rsapi.py include ChangeLog MANIFEST.in pylintrc Authors -graft CA +graft nova/CA graft doc graft smoketests graft tools From 881c774d0fbe33591c421acf87ba0a370c2e0f85 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 5 Apr 2011 14:53:56 +0200 Subject: [PATCH 8/9] Add a find_data_files method to setup.py. Use it to get tools/ installed under /usr/(local/)/share/nova --- setup.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/setup.py b/setup.py index 20f4c1947..6c45109bc 100644 --- a/setup.py +++ b/setup.py @@ -16,6 +16,7 @@ # License for the specific language governing permissions and limitations # under the License. +import glob import os import subprocess import sys @@ -86,6 +87,19 @@ try: except: pass + +def find_data_files(destdir, srcdir): + package_data = [] + files = [] + for d in glob.glob('%s/*' % (srcdir, )): + if os.path.isdir(d): + package_data += find_data_files( + os.path.join(destdir, os.path.basename(d)), d) + else: + files += [d] + package_data += [(destdir, files)] + return package_data + DistUtilsExtra.auto.setup(name='nova', version=version.canonical_version_string(), description='cloud computing fabric controller', @@ -96,6 +110,7 @@ DistUtilsExtra.auto.setup(name='nova', packages=find_packages(exclude=['bin', 'smoketests']), include_package_data=True, test_suite='nose.collector', + data_files=find_data_files('share/nova', 'tools'), scripts=['bin/nova-ajax-console-proxy', 'bin/nova-api', 'bin/nova-compute', From 3a61630c3f6185492aac945c32d235b1c3eaa2a0 Mon Sep 17 00:00:00 2001 From: Soren Hansen Date: Tue, 5 Apr 2011 14:55:19 +0200 Subject: [PATCH 9/9] Help paste_config_file find the api config now that we moved it. --- nova/wsgi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/wsgi.py b/nova/wsgi.py index ba0819466..05e7d5924 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -532,6 +532,7 @@ def paste_config_file(basename): """ configfiles = [basename, + os.path.join(FLAGS.state_path, 'etc', 'nova', basename), os.path.join(FLAGS.state_path, 'etc', basename), os.path.join(FLAGS.state_path, basename), '/etc/nova/%s' % basename]