merged trunk

This commit is contained in:
Vishvananda Ishaya
2011-04-05 14:44:39 -07:00
18 changed files with 55 additions and 689 deletions

View File

@@ -32,6 +32,7 @@ Jesse Andrews <anotherjesse@gmail.com>
Joe Heck <heckj@mac.com>
Joel Moore <joelbm24@gmail.com>
John Dewey <john@dewey.ws>
John Tran <jtran@attinteractive.com>
Jonathan Bryce <jbryce@jbryce.com>
Jordan Rinke <jordan@openstack.org>
Josh Durgin <joshd@hq.newdream.net>

11
CA/.gitignore vendored
View File

@@ -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/*

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
*

1
CA/reqs/.gitignore vendored
View File

@@ -1 +0,0 @@
*

View File

View File

@@ -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

View File

@@ -1,473 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Nova User API client library.
"""
import base64
import boto
import boto.exception
import httplib
import re
import string
from boto.ec2.regioninfo import RegionInfo
DEFAULT_CLC_URL = 'http://127.0.0.1:8773'
DEFAULT_REGION = 'nova'
class UserInfo(object):
"""
Information about a Nova user, as parsed through SAX.
**Fields Include**
* username
* accesskey
* secretkey
* file (optional) containing zip of X509 cert & rc file
"""
def __init__(self, connection=None, username=None, endpoint=None):
self.connection = connection
self.username = username
self.endpoint = endpoint
def __repr__(self):
return 'UserInfo:%s' % self.username
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'username':
self.username = str(value)
elif name == 'file':
self.file = base64.b64decode(str(value))
elif name == 'accesskey':
self.accesskey = str(value)
elif name == 'secretkey':
self.secretkey = str(value)
class UserRole(object):
"""
Information about a Nova user's role, as parsed through SAX.
**Fields include**
* role
"""
def __init__(self, connection=None):
self.connection = connection
self.role = None
def __repr__(self):
return 'UserRole:%s' % self.role
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'role':
self.role = value
else:
setattr(self, name, str(value))
class ProjectInfo(object):
"""
Information about a Nova project, as parsed through SAX.
**Fields include**
* projectname
* description
* projectManagerId
* memberIds
"""
def __init__(self, connection=None):
self.connection = connection
self.projectname = None
self.description = None
self.projectManagerId = None
self.memberIds = []
def __repr__(self):
return 'ProjectInfo:%s' % self.projectname
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'projectname':
self.projectname = value
elif name == 'description':
self.description = value
elif name == 'projectManagerId':
self.projectManagerId = value
elif name == 'memberId':
self.memberIds.append(value)
else:
setattr(self, name, str(value))
class ProjectMember(object):
"""
Information about a Nova project member, as parsed through SAX.
**Fields include**
* memberId
"""
def __init__(self, connection=None):
self.connection = connection
self.memberId = None
def __repr__(self):
return 'ProjectMember:%s' % self.memberId
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == 'member':
self.memberId = value
else:
setattr(self, name, str(value))
class HostInfo(object):
"""
Information about a Nova Host, as parsed through SAX.
**Fields Include**
* Hostname
* Compute service status
* Volume service status
* Instance count
* Volume count
"""
def __init__(self, connection=None):
self.connection = connection
self.hostname = None
self.compute = None
self.volume = None
self.instance_count = 0
self.volume_count = 0
def __repr__(self):
return 'Host:%s' % self.hostname
# this is needed by the sax parser, so ignore the ugly name
def startElement(self, name, attrs, connection):
return None
# this is needed by the sax parser, so ignore the ugly name
def endElement(self, name, value, connection):
fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name))
setattr(self, fixed_name, value)
class Vpn(object):
"""
Information about a Vpn, as parsed through SAX
**Fields Include**
* instance_id
* project_id
* public_ip
* public_port
* created_at
* internal_ip
* state
"""
def __init__(self, connection=None):
self.connection = connection
self.instance_id = None
self.project_id = None
def __repr__(self):
return 'Vpn:%s:%s' % (self.project_id, self.instance_id)
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
fixed_name = string.lower(re.sub(r'([A-Z])', r'_\1', name))
setattr(self, fixed_name, value)
class InstanceType(object):
"""
Information about a Nova instance type, as parsed through SAX.
**Fields include**
* name
* vcpus
* disk_gb
* memory_mb
* flavor_id
"""
def __init__(self, connection=None):
self.connection = connection
self.name = None
self.vcpus = None
self.disk_gb = None
self.memory_mb = None
self.flavor_id = None
def __repr__(self):
return 'InstanceType:%s' % self.name
def startElement(self, name, attrs, connection):
return None
def endElement(self, name, value, connection):
if name == "memoryMb":
self.memory_mb = str(value)
elif name == "flavorId":
self.flavor_id = str(value)
elif name == "diskGb":
self.disk_gb = str(value)
else:
setattr(self, name, str(value))
class NovaAdminClient(object):
def __init__(
self,
clc_url=DEFAULT_CLC_URL,
region=DEFAULT_REGION,
access_key=None,
secret_key=None,
**kwargs):
parts = self.split_clc_url(clc_url)
self.clc_url = clc_url
self.region = region
self.access = access_key
self.secret = secret_key
self.apiconn = boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
region=RegionInfo(None,
region,
parts['ip']),
port=parts['port'],
path='/services/Admin',
**kwargs)
self.apiconn.APIVersion = 'nova'
def connection_for(self, username, project, clc_url=None, region=None,
**kwargs):
"""Returns a boto ec2 connection for the given username."""
if not clc_url:
clc_url = self.clc_url
if not region:
region = self.region
parts = self.split_clc_url(clc_url)
user = self.get_user(username)
access_key = '%s:%s' % (user.accesskey, project)
return boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=user.secretkey,
is_secure=parts['is_secure'],
region=RegionInfo(None,
self.region,
parts['ip']),
port=parts['port'],
path='/services/Cloud',
**kwargs)
def split_clc_url(self, clc_url):
"""Splits a cloud controller endpoint url."""
parts = httplib.urlsplit(clc_url)
is_secure = parts.scheme == 'https'
ip, port = parts.netloc.split(':')
return {'ip': ip, 'port': int(port), 'is_secure': is_secure}
def get_users(self):
"""Grabs the list of all users."""
return self.apiconn.get_list('DescribeUsers', {}, [('item', UserInfo)])
def get_user(self, name):
"""Grab a single user by name."""
user = self.apiconn.get_object('DescribeUser',
{'Name': name},
UserInfo)
if user.username != None:
return user
def has_user(self, username):
"""Determine if user exists."""
return self.get_user(username) != None
def create_user(self, username):
"""Creates a new user, returning the userinfo object with
access/secret."""
return self.apiconn.get_object('RegisterUser', {'Name': username},
UserInfo)
def delete_user(self, username):
"""Deletes a user."""
return self.apiconn.get_object('DeregisterUser', {'Name': username},
UserInfo)
def get_roles(self, project_roles=True):
"""Returns a list of available roles."""
return self.apiconn.get_list('DescribeRoles',
{'ProjectRoles': project_roles},
[('item', UserRole)])
def get_user_roles(self, user, project=None):
"""Returns a list of roles for the given user.
Omitting project will return any global roles that the user has.
Specifying project will return only project specific roles.
"""
params = {'User': user}
if project:
params['Project'] = project
return self.apiconn.get_list('DescribeUserRoles',
params,
[('item', UserRole)])
def add_user_role(self, user, role, project=None):
"""Add a role to a user either globally or for a specific project."""
return self.modify_user_role(user, role, project=project,
operation='add')
def remove_user_role(self, user, role, project=None):
"""Remove a role from a user either globally or for a specific
project."""
return self.modify_user_role(user, role, project=project,
operation='remove')
def modify_user_role(self, user, role, project=None, operation='add',
**kwargs):
"""Add or remove a role for a user and project."""
params = {'User': user,
'Role': role,
'Project': project,
'Operation': operation}
return self.apiconn.get_status('ModifyUserRole', params)
def get_projects(self, user=None):
"""Returns a list of all projects."""
if user:
params = {'User': user}
else:
params = {}
return self.apiconn.get_list('DescribeProjects',
params,
[('item', ProjectInfo)])
def get_project(self, name):
"""Returns a single project with the specified name."""
project = self.apiconn.get_object('DescribeProject',
{'Name': name},
ProjectInfo)
if project.projectname != None:
return project
def create_project(self, projectname, manager_user, description=None,
member_users=None):
"""Creates a new project."""
params = {'Name': projectname,
'ManagerUser': manager_user,
'Description': description,
'MemberUsers': member_users}
return self.apiconn.get_object('RegisterProject', params, ProjectInfo)
def modify_project(self, projectname, manager_user=None, description=None):
"""Modifies an existing project."""
params = {'Name': projectname,
'ManagerUser': manager_user,
'Description': description}
return self.apiconn.get_status('ModifyProject', params)
def delete_project(self, projectname):
"""Permanently deletes the specified project."""
return self.apiconn.get_object('DeregisterProject',
{'Name': projectname},
ProjectInfo)
def get_project_members(self, name):
"""Returns a list of members of a project."""
return self.apiconn.get_list('DescribeProjectMembers',
{'Name': name},
[('item', ProjectMember)])
def add_project_member(self, user, project):
"""Adds a user to a project."""
return self.modify_project_member(user, project, operation='add')
def remove_project_member(self, user, project):
"""Removes a user from a project."""
return self.modify_project_member(user, project, operation='remove')
def modify_project_member(self, user, project, operation='add'):
"""Adds or removes a user from a project."""
params = {'User': user,
'Project': project,
'Operation': operation}
return self.apiconn.get_status('ModifyProjectMember', params)
def get_zip(self, user, project):
"""Returns the content of a zip file containing novarc and access
credentials."""
params = {'Name': user, 'Project': project}
zip = self.apiconn.get_object('GenerateX509ForUser', params, UserInfo)
return zip.file
def start_vpn(self, project):
"""
Starts the vpn for a user
"""
return self.apiconn.get_object('StartVpn', {'Project': project}, Vpn)
def get_vpns(self):
"""Return a list of vpn with project name"""
return self.apiconn.get_list('DescribeVpns', {}, [('item', Vpn)])
def get_hosts(self):
return self.apiconn.get_list('DescribeHosts', {}, [('item', HostInfo)])
def get_instance_types(self):
"""Grabs the list of all users."""
return self.apiconn.get_list('DescribeInstanceTypes', {},
[('item', InstanceType)])

View File

@@ -74,7 +74,12 @@ class Connection(carrot_connection.BrokerConnection):
"""Recreates the connection instance
This is necessary to recover from some network errors/disconnects"""
del cls._instance
try:
del cls._instance
except AttributeError, e:
# The _instance stuff is for testing purposes. Usually we don't use
# it. So don't freak out if it doesn't exist.
pass
return cls.instance()
@@ -125,9 +130,9 @@ class Consumer(messaging.Consumer):
# NOTE(vish): This is catching all errors because we really don't
# want exceptions to be logged 10 times a second if some
# persistent failure occurs.
except Exception: # pylint: disable=W0703
except Exception, e: # pylint: disable=W0703
if not self.failed_connection:
LOG.exception(_("Failed to fetch message from queue"))
LOG.exception(_("Failed to fetch message from queue: %s" % e))
self.failed_connection = True
def attach_to_eventlet(self):

View File

@@ -72,7 +72,9 @@ class SimpleScheduler(chance.ChanceScheduler):
{'host': service['host'],
'scheduled_at': now})
return service['host']
raise driver.NoValidHost(_("No hosts found"))
raise driver.NoValidHost(_("Scheduler was unable to locate a host"
" for this request. Is the appropriate"
" service running?"))
def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest volumes."""
@@ -107,7 +109,9 @@ class SimpleScheduler(chance.ChanceScheduler):
{'host': service['host'],
'scheduled_at': now})
return service['host']
raise driver.NoValidHost(_("No hosts found"))
raise driver.NoValidHost(_("Scheduler was unable to locate a host"
" for this request. Is the appropriate"
" service running?"))
def schedule_set_network_host(self, context, *_args, **_kwargs):
"""Picks a host that is up and has the fewest networks."""
@@ -119,4 +123,6 @@ class SimpleScheduler(chance.ChanceScheduler):
raise driver.NoValidHost(_("All hosts have too many networks"))
if self.service_is_up(service):
return service['host']
raise driver.NoValidHost(_("No hosts found"))
raise driver.NoValidHost(_("Scheduler was unable to locate a host"
" for this request. Is the appropriate"
" service running?"))

View File

@@ -52,5 +52,8 @@ class ZoneScheduler(driver.Scheduler):
zone = _kwargs.get('availability_zone')
hosts = self.hosts_up_with_zone(context, topic, zone)
if not hosts:
raise driver.NoValidHost(_("No hosts found"))
raise driver.NoValidHost(_("Scheduler was unable to locate a host"
" for this request. Is the appropriate"
" service running?"))
return hosts[int(random.random() * len(hosts))]

View File

@@ -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)
@@ -216,6 +218,35 @@ class CloudTestCase(test.TestCase):
db.service_destroy(self.context, comp1['id'])
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'}}]
def fake_show_none(meh, context, id):
raise NotFound
self.stubs.Set(local.LocalImageService, 'detail', fake_detail)
# 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
max_count = 1