merged trunk
This commit is contained in:
		@@ -266,6 +266,18 @@ class UserCommands(object):
 | 
			
		||||
        for user in self.manager.get_users():
 | 
			
		||||
            print user.name
 | 
			
		||||
 | 
			
		||||
    def modify(self, name, access_key, secret_key, is_admin):
 | 
			
		||||
        """update a users keys & admin flag
 | 
			
		||||
        arguments: accesskey secretkey admin
 | 
			
		||||
        leave any field blank to ignore it, admin should be 'T', 'F', or blank
 | 
			
		||||
        """
 | 
			
		||||
        if not is_admin:
 | 
			
		||||
            is_admin = None
 | 
			
		||||
        elif is_admin.upper()[0] == 'T':
 | 
			
		||||
            is_admin = True
 | 
			
		||||
        else:
 | 
			
		||||
            is_admin = False
 | 
			
		||||
        self.manager.modify_user(name, access_key, secret_key, is_admin)
 | 
			
		||||
 | 
			
		||||
class ProjectCommands(object):
 | 
			
		||||
    """Class for managing projects."""
 | 
			
		||||
@@ -291,7 +303,7 @@ class ProjectCommands(object):
 | 
			
		||||
    def environment(self, project_id, user_id, filename='novarc'):
 | 
			
		||||
        """Exports environment variables to an sourcable file
 | 
			
		||||
        arguments: project_id user_id [filename='novarc]"""
 | 
			
		||||
        rc = self.manager.get_environment_rc(project_id, user_id)
 | 
			
		||||
        rc = self.manager.get_environment_rc(user_id, project_id)
 | 
			
		||||
        with open(filename, 'w') as f:
 | 
			
		||||
            f.write(rc)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								nova/api/cloud.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								nova/api/cloud.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Methods for API calls to control instances via AMQP.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from nova import db
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import rpc
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def reboot(instance_id, context=None):
 | 
			
		||||
    """Reboot the given instance.
 | 
			
		||||
    
 | 
			
		||||
    #TODO(gundlach) not actually sure what context is used for by ec2 here
 | 
			
		||||
    -- I think we can just remove it and use None all the time.
 | 
			
		||||
    """
 | 
			
		||||
    instance_ref = db.instance_get_by_ec2_id(None, instance_id)
 | 
			
		||||
    host = instance_ref['host']
 | 
			
		||||
    rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host),
 | 
			
		||||
             {"method": "reboot_instance",
 | 
			
		||||
              "args": {"context": None,
 | 
			
		||||
                       "instance_id": instance_ref['id']}})
 | 
			
		||||
@@ -158,12 +158,14 @@ class Authorizer(wsgi.Middleware):
 | 
			
		||||
                'RunInstances': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'TerminateInstances': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'RebootInstances': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'UpdateInstance': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'DeleteVolume': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'DescribeImages': ['all'],
 | 
			
		||||
                'DeregisterImage': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'RegisterImage': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'DescribeImageAttribute': ['all'],
 | 
			
		||||
                'ModifyImageAttribute': ['projectmanager', 'sysadmin'],
 | 
			
		||||
                'UpdateImage': ['projectmanager', 'sysadmin'],
 | 
			
		||||
            },
 | 
			
		||||
            'AdminController': {
 | 
			
		||||
                # All actions have the same permission: ['none'] (the default)
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ from nova import quota
 | 
			
		||||
from nova import rpc
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova.compute.instance_types import INSTANCE_TYPES
 | 
			
		||||
from nova.api import cloud
 | 
			
		||||
from nova.api.ec2 import images
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -285,6 +286,9 @@ class CloudController(object):
 | 
			
		||||
                                   'volume_id': volume['ec2_id']}]
 | 
			
		||||
        else:
 | 
			
		||||
            v['attachmentSet'] = [{}]
 | 
			
		||||
 | 
			
		||||
        v['display_name'] = volume['display_name']
 | 
			
		||||
        v['display_description'] = volume['display_description']
 | 
			
		||||
        return v
 | 
			
		||||
 | 
			
		||||
    def create_volume(self, context, size, **kwargs):
 | 
			
		||||
@@ -302,6 +306,8 @@ class CloudController(object):
 | 
			
		||||
        vol['availability_zone'] = FLAGS.storage_availability_zone
 | 
			
		||||
        vol['status'] = "creating"
 | 
			
		||||
        vol['attach_status'] = "detached"
 | 
			
		||||
        vol['display_name'] = kwargs.get('display_name')
 | 
			
		||||
        vol['display_description'] = kwargs.get('display_description')
 | 
			
		||||
        volume_ref = db.volume_create(context, vol)
 | 
			
		||||
 | 
			
		||||
        rpc.cast(FLAGS.scheduler_topic,
 | 
			
		||||
@@ -368,6 +374,16 @@ class CloudController(object):
 | 
			
		||||
            lst = [lst]
 | 
			
		||||
        return [{label: x} for x in lst]
 | 
			
		||||
 | 
			
		||||
    def update_volume(self, context, volume_id, **kwargs):
 | 
			
		||||
        updatable_fields = ['display_name', 'display_description']
 | 
			
		||||
        changes = {}
 | 
			
		||||
        for field in updatable_fields:
 | 
			
		||||
            if field in kwargs:
 | 
			
		||||
                changes[field] = kwargs[field]
 | 
			
		||||
        if changes:
 | 
			
		||||
            db.volume_update(context, volume_id, kwargs)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def describe_instances(self, context, **kwargs):
 | 
			
		||||
        return self._format_describe_instances(context)
 | 
			
		||||
 | 
			
		||||
@@ -420,6 +436,8 @@ class CloudController(object):
 | 
			
		||||
            i['instanceType'] = instance['instance_type']
 | 
			
		||||
            i['launchTime'] = instance['created_at']
 | 
			
		||||
            i['amiLaunchIndex'] = instance['launch_index']
 | 
			
		||||
            i['displayName'] = instance['display_name']
 | 
			
		||||
            i['displayDescription'] = instance['display_description']
 | 
			
		||||
            if not reservations.has_key(instance['reservation_id']):
 | 
			
		||||
                r = {}
 | 
			
		||||
                r['reservationId'] = instance['reservation_id']
 | 
			
		||||
@@ -577,6 +595,8 @@ class CloudController(object):
 | 
			
		||||
        base_options['user_data'] = kwargs.get('user_data', '')
 | 
			
		||||
        base_options['security_group'] = security_group
 | 
			
		||||
        base_options['instance_type'] = instance_type
 | 
			
		||||
        base_options['display_name'] = kwargs.get('display_name')
 | 
			
		||||
        base_options['display_description'] = kwargs.get('display_description')
 | 
			
		||||
 | 
			
		||||
        type_data = INSTANCE_TYPES[instance_type]
 | 
			
		||||
        base_options['memory_mb'] = type_data['memory_mb']
 | 
			
		||||
@@ -665,12 +685,19 @@ class CloudController(object):
 | 
			
		||||
    def reboot_instances(self, context, instance_id, **kwargs):
 | 
			
		||||
        """instance_id is a list of instance ids"""
 | 
			
		||||
        for id_str in instance_id:
 | 
			
		||||
            instance_ref = db.instance_get_by_ec2_id(context, id_str)
 | 
			
		||||
            host = instance_ref['host']
 | 
			
		||||
            rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host),
 | 
			
		||||
                     {"method": "reboot_instance",
 | 
			
		||||
                      "args": {"context": None,
 | 
			
		||||
                               "instance_id": instance_ref['id']}})
 | 
			
		||||
            cloud.reboot(id_str, context=context)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def update_instance(self, context, instance_id, **kwargs):
 | 
			
		||||
        updatable_fields = ['display_name', 'display_description']
 | 
			
		||||
        changes = {}
 | 
			
		||||
        for field in updatable_fields:
 | 
			
		||||
            if field in kwargs:
 | 
			
		||||
                changes[field] = kwargs[field]
 | 
			
		||||
        if changes:
 | 
			
		||||
            db_context = {}
 | 
			
		||||
            inst = db.instance_get_by_ec2_id(db_context, instance_id)
 | 
			
		||||
            db.instance_update(db_context, inst['id'], kwargs)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def delete_volume(self, context, volume_id, **kwargs):
 | 
			
		||||
@@ -728,3 +755,7 @@ class CloudController(object):
 | 
			
		||||
        if not operation_type in ['add', 'remove']:
 | 
			
		||||
            raise exception.ApiError('operation_type must be add or remove')
 | 
			
		||||
        return images.modify(context, image_id, operation_type)
 | 
			
		||||
 | 
			
		||||
    def update_image(self, context, image_id, **kwargs):
 | 
			
		||||
        result = images.update(context, image_id, dict(kwargs))
 | 
			
		||||
        return result
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,14 @@ def modify(context, image_id, operation):
 | 
			
		||||
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
def update(context, image_id, attributes):
 | 
			
		||||
    """update an image's attributes / info.json"""
 | 
			
		||||
    attributes.update({"image_id": image_id})
 | 
			
		||||
    conn(context).make_request(
 | 
			
		||||
        method='POST',
 | 
			
		||||
        bucket='_images',
 | 
			
		||||
        query_args=qs(attributes))
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
def register(context, image_location):
 | 
			
		||||
    """ rpc call to register a new image based from a manifest """
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ import webob
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
from nova.api.rackspace import backup_schedules
 | 
			
		||||
from nova.api.rackspace import flavors
 | 
			
		||||
from nova.api.rackspace import images
 | 
			
		||||
@@ -67,7 +68,7 @@ class AuthMiddleware(wsgi.Middleware):
 | 
			
		||||
        user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"])
 | 
			
		||||
 | 
			
		||||
        if not user:
 | 
			
		||||
            return webob.exc.HTTPUnauthorized()
 | 
			
		||||
            return faults.Fault(webob.exc.HTTPUnauthorized())
 | 
			
		||||
 | 
			
		||||
        if not req.environ.has_key('nova.context'):
 | 
			
		||||
            req.environ['nova.context'] = {}
 | 
			
		||||
@@ -112,8 +113,10 @@ class RateLimitingMiddleware(wsgi.Middleware):
 | 
			
		||||
        delay = self.get_delay(action_name, username)
 | 
			
		||||
        if delay:
 | 
			
		||||
            # TODO(gundlach): Get the retry-after format correct.
 | 
			
		||||
            raise webob.exc.HTTPRequestEntityTooLarge(headers={
 | 
			
		||||
                    'Retry-After': time.time() + delay})
 | 
			
		||||
            exc = webob.exc.HTTPRequestEntityTooLarge(
 | 
			
		||||
                    explanation='Too many requests.',
 | 
			
		||||
                    headers={'Retry-After': time.time() + delay})
 | 
			
		||||
            raise faults.Fault(exc)
 | 
			
		||||
        return self.application
 | 
			
		||||
 | 
			
		||||
    def get_delay(self, action_name, username):
 | 
			
		||||
@@ -165,3 +168,23 @@ class APIRouter(wsgi.Router):
 | 
			
		||||
                        controller=sharedipgroups.Controller())
 | 
			
		||||
 | 
			
		||||
        super(APIRouter, self).__init__(mapper)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def limited(items, req):
 | 
			
		||||
    """Return a slice of items according to requested offset and limit.
 | 
			
		||||
    
 | 
			
		||||
    items - a sliceable
 | 
			
		||||
    req - wobob.Request possibly containing offset and limit GET variables.
 | 
			
		||||
          offset is where to start in the list, and limit is the maximum number
 | 
			
		||||
          of items to return.
 | 
			
		||||
 | 
			
		||||
    If limit is not specified, 0, or > 1000, defaults to 1000.
 | 
			
		||||
    """
 | 
			
		||||
    offset = int(req.GET.get('offset', 0))
 | 
			
		||||
    limit = int(req.GET.get('limit', 0))
 | 
			
		||||
    if not limit:
 | 
			
		||||
        limit = 1000
 | 
			
		||||
    limit = min(1000, limit)
 | 
			
		||||
    range_end = offset + limit
 | 
			
		||||
    return items[offset:range_end]
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ from nova import db
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import manager
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
@@ -36,13 +37,13 @@ class BasicApiAuthManager(object):
 | 
			
		||||
        # honor it
 | 
			
		||||
        path_info = req.path_info
 | 
			
		||||
        if len(path_info) > 1:
 | 
			
		||||
            return webob.exc.HTTPUnauthorized()
 | 
			
		||||
            return faults.Fault(webob.exc.HTTPUnauthorized())
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            username, key = req.headers['X-Auth-User'], \
 | 
			
		||||
                req.headers['X-Auth-Key']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            return webob.exc.HTTPUnauthorized()
 | 
			
		||||
            return faults.Fault(webob.exc.HTTPUnauthorized())
 | 
			
		||||
 | 
			
		||||
        username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key']
 | 
			
		||||
        token, user = self._authorize_user(username, key)
 | 
			
		||||
@@ -57,7 +58,7 @@ class BasicApiAuthManager(object):
 | 
			
		||||
            res.status = '204'
 | 
			
		||||
            return res
 | 
			
		||||
        else:
 | 
			
		||||
            return webob.exc.HTTPUnauthorized()
 | 
			
		||||
            return faults.Fault(webob.exc.HTTPUnauthorized())
 | 
			
		||||
 | 
			
		||||
    def authorize_token(self, token_hash):
 | 
			
		||||
        """ retrieves user information from the datastore given a token
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ from webob import exc
 | 
			
		||||
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
from nova.api.rackspace import _id_translator
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
import nova.image.service
 | 
			
		||||
 | 
			
		||||
class Controller(wsgi.Controller):
 | 
			
		||||
@@ -27,12 +28,12 @@ class Controller(wsgi.Controller):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def index(self, req, server_id):
 | 
			
		||||
        return exc.HTTPNotFound()
 | 
			
		||||
        return faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def create(self, req, server_id):
 | 
			
		||||
        """ No actual update method required, since the existing API allows
 | 
			
		||||
        both create and update through a POST """
 | 
			
		||||
        return exc.HTTPNotFound()
 | 
			
		||||
        return faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def delete(self, req, server_id):
 | 
			
		||||
        return exc.HTTPNotFound()
 | 
			
		||||
        return faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								nova/api/rackspace/context.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								nova/api/rackspace/context.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# Copyright 2010 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.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
APIRequestContext
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import random
 | 
			
		||||
 | 
			
		||||
class Project(object):
 | 
			
		||||
    def __init__(self, user_id):
 | 
			
		||||
        self.id = user_id
 | 
			
		||||
    
 | 
			
		||||
class APIRequestContext(object):
 | 
			
		||||
    """ This is an adapter class to get around all of the assumptions made in
 | 
			
		||||
    the FlatNetworking """
 | 
			
		||||
    def __init__(self, user_id):
 | 
			
		||||
        self.user_id = user_id
 | 
			
		||||
        self.project = Project(user_id)
 | 
			
		||||
							
								
								
									
										62
									
								
								nova/api/rackspace/faults.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								nova/api/rackspace/faults.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
			
		||||
 | 
			
		||||
# Copyright 2010 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 webob.dec
 | 
			
		||||
import webob.exc
 | 
			
		||||
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Fault(webob.exc.HTTPException):
 | 
			
		||||
 | 
			
		||||
    """An RS API fault response."""
 | 
			
		||||
 | 
			
		||||
    _fault_names = {
 | 
			
		||||
            400: "badRequest",
 | 
			
		||||
            401: "unauthorized",
 | 
			
		||||
            403: "resizeNotAllowed",
 | 
			
		||||
            404: "itemNotFound",
 | 
			
		||||
            405: "badMethod",
 | 
			
		||||
            409: "inProgress",
 | 
			
		||||
            413: "overLimit",
 | 
			
		||||
            415: "badMediaType",
 | 
			
		||||
            501: "notImplemented",
 | 
			
		||||
            503: "serviceUnavailable"}
 | 
			
		||||
 | 
			
		||||
    def __init__(self, exception):
 | 
			
		||||
        """Create a Fault for the given webob.exc.exception."""
 | 
			
		||||
        self.wrapped_exc = exception
 | 
			
		||||
 | 
			
		||||
    @webob.dec.wsgify
 | 
			
		||||
    def __call__(self, req):
 | 
			
		||||
        """Generate a WSGI response based on the exception passed to ctor."""
 | 
			
		||||
        # Replace the body with fault details.
 | 
			
		||||
        code = self.wrapped_exc.status_int
 | 
			
		||||
        fault_name = self._fault_names.get(code, "cloudServersFault")
 | 
			
		||||
        fault_data = {
 | 
			
		||||
            fault_name: {
 | 
			
		||||
                'code': code,
 | 
			
		||||
                'message': self.wrapped_exc.explanation}}
 | 
			
		||||
        if code == 413:
 | 
			
		||||
            retry = self.wrapped_exc.headers['Retry-After']
 | 
			
		||||
            fault_data[fault_name]['retryAfter'] = retry
 | 
			
		||||
        # 'code' is an attribute on the fault tag itself 
 | 
			
		||||
        metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
 | 
			
		||||
        serializer = wsgi.Serializer(req.environ, metadata)
 | 
			
		||||
        self.wrapped_exc.body = serializer.to_content_type(fault_data)
 | 
			
		||||
        return self.wrapped_exc
 | 
			
		||||
@@ -15,9 +15,12 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
from webob import exc
 | 
			
		||||
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
from nova.compute import instance_types
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
from webob import exc
 | 
			
		||||
import nova.api.rackspace
 | 
			
		||||
 | 
			
		||||
class Controller(wsgi.Controller):
 | 
			
		||||
    """Flavor controller for the Rackspace API."""
 | 
			
		||||
@@ -38,6 +41,7 @@ class Controller(wsgi.Controller):
 | 
			
		||||
    def detail(self, req):
 | 
			
		||||
        """Return all flavors in detail."""
 | 
			
		||||
        items = [self.show(req, id)['flavor'] for id in self._all_ids()]
 | 
			
		||||
        items = nova.api.rackspace.limited(items, req)
 | 
			
		||||
        return dict(flavors=items)
 | 
			
		||||
 | 
			
		||||
    def show(self, req, id):
 | 
			
		||||
@@ -47,7 +51,7 @@ class Controller(wsgi.Controller):
 | 
			
		||||
                item = dict(ram=val['memory_mb'], disk=val['local_gb'],
 | 
			
		||||
                            id=val['flavorid'], name=name)
 | 
			
		||||
                return dict(flavor=item)
 | 
			
		||||
        raise exc.HTTPNotFound()
 | 
			
		||||
        raise faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def _all_ids(self):
 | 
			
		||||
        """Return the list of all flavorids."""
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,9 @@ from webob import exc
 | 
			
		||||
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
from nova.api.rackspace import _id_translator
 | 
			
		||||
import nova.api.rackspace
 | 
			
		||||
import nova.image.service
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
 | 
			
		||||
class Controller(wsgi.Controller):
 | 
			
		||||
 | 
			
		||||
@@ -45,6 +47,7 @@ class Controller(wsgi.Controller):
 | 
			
		||||
    def detail(self, req):
 | 
			
		||||
        """Return all public images in detail."""
 | 
			
		||||
        data = self._service.index()
 | 
			
		||||
        data = nova.api.rackspace.limited(data, req)
 | 
			
		||||
        for img in data:
 | 
			
		||||
            img['id'] = self._id_translator.to_rs_id(img['id'])
 | 
			
		||||
        return dict(images=data)
 | 
			
		||||
@@ -58,14 +61,14 @@ class Controller(wsgi.Controller):
 | 
			
		||||
 | 
			
		||||
    def delete(self, req, id):
 | 
			
		||||
        # Only public images are supported for now.
 | 
			
		||||
        raise exc.HTTPNotFound()
 | 
			
		||||
        raise faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def create(self, req):
 | 
			
		||||
        # Only public images are supported for now, so a request to
 | 
			
		||||
        # make a backup of a server cannot be supproted.
 | 
			
		||||
        raise exc.HTTPNotFound()
 | 
			
		||||
        raise faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def update(self, req, id):
 | 
			
		||||
        # Users may not modify public images, and that's all that 
 | 
			
		||||
        # we support for now.
 | 
			
		||||
        raise exc.HTTPNotFound()
 | 
			
		||||
        raise faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 
 | 
			
		||||
@@ -17,33 +17,45 @@
 | 
			
		||||
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
import webob
 | 
			
		||||
from webob import exc
 | 
			
		||||
 | 
			
		||||
from nova import flags
 | 
			
		||||
from nova import rpc
 | 
			
		||||
from nova import utils
 | 
			
		||||
from nova import wsgi
 | 
			
		||||
from nova.api import cloud
 | 
			
		||||
from nova.api.rackspace import _id_translator
 | 
			
		||||
from nova.api.rackspace import context
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
from nova.compute import instance_types
 | 
			
		||||
from nova.compute import power_state
 | 
			
		||||
import nova.api.rackspace
 | 
			
		||||
import nova.image.service
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
flags.DEFINE_string('rs_network_manager', 'nova.network.manager.FlatManager',
 | 
			
		||||
    'Networking for rackspace')
 | 
			
		||||
 | 
			
		||||
def _instance_id_translator():
 | 
			
		||||
    """ Helper method for initializing an id translator for Rackspace instance
 | 
			
		||||
    ids """
 | 
			
		||||
    return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova')
 | 
			
		||||
 | 
			
		||||
def translator_instance():
 | 
			
		||||
def _image_service():
 | 
			
		||||
    """ Helper method for initializing the image id translator """
 | 
			
		||||
    service = nova.image.service.ImageService.load()
 | 
			
		||||
    return _id_translator.RackspaceAPIIdTranslator(
 | 
			
		||||
            "image", service.__class__.__name__)
 | 
			
		||||
    return (service, _id_translator.RackspaceAPIIdTranslator(
 | 
			
		||||
            "image", service.__class__.__name__))
 | 
			
		||||
 | 
			
		||||
def _filter_params(inst_dict):
 | 
			
		||||
    """ Extracts all updatable parameters for a server update request """
 | 
			
		||||
    keys = ['name', 'adminPass']
 | 
			
		||||
    keys = dict(name='name', admin_pass='adminPass')
 | 
			
		||||
    new_attrs = {}
 | 
			
		||||
    for k in keys:
 | 
			
		||||
        if inst_dict.has_key(k):
 | 
			
		||||
            new_attrs[k] = inst_dict[k]
 | 
			
		||||
    for k, v in keys.items():
 | 
			
		||||
        if inst_dict.has_key(v):
 | 
			
		||||
            new_attrs[k] = inst_dict[v]
 | 
			
		||||
    return new_attrs
 | 
			
		||||
 | 
			
		||||
def _entity_list(entities):
 | 
			
		||||
@@ -83,7 +95,6 @@ def _entity_inst(inst):
 | 
			
		||||
class Controller(wsgi.Controller):
 | 
			
		||||
    """ The Server API controller for the Openstack API """
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    _serialization_metadata = {
 | 
			
		||||
        'application/xml': {
 | 
			
		||||
            "attributes": {
 | 
			
		||||
@@ -101,42 +112,58 @@ class Controller(wsgi.Controller):
 | 
			
		||||
 | 
			
		||||
    def index(self, req):
 | 
			
		||||
        """ Returns a list of server names and ids for a given user """
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
        instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
 | 
			
		||||
        res = [_entity_inst(inst)['server'] for inst in instance_list]
 | 
			
		||||
        return _entity_list(res)
 | 
			
		||||
        return self._items(req, entity_maker=_entity_inst)
 | 
			
		||||
 | 
			
		||||
    def detail(self, req):
 | 
			
		||||
        """ Returns a list of server details for a given user """
 | 
			
		||||
        return self._items(req, entity_maker=_entity_detail)
 | 
			
		||||
 | 
			
		||||
    def _items(self, req, entity_maker):
 | 
			
		||||
        """Returns a list of servers for a given user.
 | 
			
		||||
 | 
			
		||||
        entity_maker - either _entity_detail or _entity_inst
 | 
			
		||||
        """
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
        res = [_entity_detail(inst)['server'] for inst in 
 | 
			
		||||
                self.db_driver.instance_get_all_by_user(None, user_id)]
 | 
			
		||||
        instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
 | 
			
		||||
        limited_list = nova.api.rackspace.limited(instance_list, req)
 | 
			
		||||
        res = [entity_maker(inst)['server'] for inst in limited_list]
 | 
			
		||||
        return _entity_list(res)
 | 
			
		||||
 | 
			
		||||
    def show(self, req, id):
 | 
			
		||||
        """ Returns server details by server id """
 | 
			
		||||
        inst_id_trans = _instance_id_translator()
 | 
			
		||||
        inst_id = inst_id_trans.from_rs_id(id)
 | 
			
		||||
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
        inst = self.db_driver.instance_get(None, id)
 | 
			
		||||
        inst = self.db_driver.instance_get_by_ec2_id(None, inst_id)
 | 
			
		||||
        if inst:
 | 
			
		||||
            if inst.user_id == user_id:
 | 
			
		||||
                return _entity_detail(inst)
 | 
			
		||||
        raise exc.HTTPNotFound()
 | 
			
		||||
        raise faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def delete(self, req, id):
 | 
			
		||||
        """ Destroys a server """
 | 
			
		||||
        inst_id_trans = _instance_id_translator()
 | 
			
		||||
        inst_id = inst_id_trans.from_rs_id(id)
 | 
			
		||||
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
        instance = self.db_driver.instance_get(None, id)
 | 
			
		||||
        instance = self.db_driver.instance_get_by_ec2_id(None, inst_id)
 | 
			
		||||
        if instance and instance['user_id'] == user_id:
 | 
			
		||||
            self.db_driver.instance_destroy(None, id)
 | 
			
		||||
            return exc.HTTPAccepted()
 | 
			
		||||
        return exc.HTTPNotFound()
 | 
			
		||||
            return faults.Fault(exc.HTTPAccepted())
 | 
			
		||||
        return faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
    def create(self, req):
 | 
			
		||||
        """ Creates a new server for a given user """
 | 
			
		||||
        if not req.environ.has_key('inst_dict'):
 | 
			
		||||
            return exc.HTTPUnprocessableEntity()
 | 
			
		||||
 | 
			
		||||
        inst = self._build_server_instance(req)
 | 
			
		||||
        env = self._deserialize(req.body, req)
 | 
			
		||||
        if not env:
 | 
			
		||||
            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            inst = self._build_server_instance(req, env)
 | 
			
		||||
        except Exception, e:
 | 
			
		||||
            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
			
		||||
 | 
			
		||||
        rpc.cast(
 | 
			
		||||
            FLAGS.compute_topic, {
 | 
			
		||||
@@ -146,62 +173,127 @@ class Controller(wsgi.Controller):
 | 
			
		||||
 | 
			
		||||
    def update(self, req, id):
 | 
			
		||||
        """ Updates the server name or password """
 | 
			
		||||
        if not req.environ.has_key('inst_dict'):
 | 
			
		||||
            return exc.HTTPUnprocessableEntity()
 | 
			
		||||
        inst_id_trans = _instance_id_translator()
 | 
			
		||||
        inst_id = inst_id_trans.from_rs_id(id)
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
 | 
			
		||||
        instance = self.db_driver.instance_get(None, id)
 | 
			
		||||
        if not instance:
 | 
			
		||||
            return exc.HTTPNotFound()
 | 
			
		||||
        inst_dict = self._deserialize(req.body, req)
 | 
			
		||||
        
 | 
			
		||||
        attrs = req.environ['nova.context'].get('model_attributes', None)
 | 
			
		||||
        if attrs:
 | 
			
		||||
            self.db_driver.instance_update(None, id, _filter_params(attrs))
 | 
			
		||||
        return exc.HTTPNoContent()
 | 
			
		||||
        if not inst_dict:
 | 
			
		||||
            return faults.Fault(exc.HTTPUnprocessableEntity())
 | 
			
		||||
 | 
			
		||||
        instance = self.db_driver.instance_get_by_ec2_id(None, inst_id)
 | 
			
		||||
        if not instance or instance.user_id != user_id:
 | 
			
		||||
            return faults.Fault(exc.HTTPNotFound())
 | 
			
		||||
 | 
			
		||||
        self.db_driver.instance_update(None, id, 
 | 
			
		||||
            _filter_params(inst_dict['server']))
 | 
			
		||||
        return faults.Fault(exc.HTTPNoContent())
 | 
			
		||||
 | 
			
		||||
    def action(self, req, id):
 | 
			
		||||
        """ multi-purpose method used to reboot, rebuild, and 
 | 
			
		||||
        resize a server """
 | 
			
		||||
        if not req.environ.has_key('inst_dict'):
 | 
			
		||||
            return exc.HTTPUnprocessableEntity()
 | 
			
		||||
        input_dict = self._deserialize(req.body, req)
 | 
			
		||||
        try:
 | 
			
		||||
            reboot_type = input_dict['reboot']['type']
 | 
			
		||||
        except Exception:
 | 
			
		||||
            raise faults.Fault(webob.exc.HTTPNotImplemented())
 | 
			
		||||
        opaque_id = _instance_id_translator().from_rs_id(id)
 | 
			
		||||
        cloud.reboot(opaque_id)
 | 
			
		||||
 | 
			
		||||
    def _build_server_instance(self, req):
 | 
			
		||||
    def _build_server_instance(self, req, env):
 | 
			
		||||
        """Build instance data structure and save it to the data store."""
 | 
			
		||||
        ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
 | 
			
		||||
        inst = {}
 | 
			
		||||
 | 
			
		||||
        env = req.environ['inst_dict']
 | 
			
		||||
 | 
			
		||||
        image_id = env['server']['imageId']
 | 
			
		||||
        opaque_id = translator_instance().from_rs_id(image_id)
 | 
			
		||||
 | 
			
		||||
        inst['name'] = env['server']['server_name']
 | 
			
		||||
        inst['image_id'] = opaque_id
 | 
			
		||||
        inst['instance_type'] = env['server']['flavorId']
 | 
			
		||||
        inst_id_trans = _instance_id_translator()
 | 
			
		||||
 | 
			
		||||
        user_id = req.environ['nova.context']['user']['id']
 | 
			
		||||
        inst['user_id'] = user_id
 | 
			
		||||
 | 
			
		||||
        flavor_id = env['server']['flavorId']
 | 
			
		||||
 | 
			
		||||
        instance_type, flavor = [(k, v) for k, v in
 | 
			
		||||
            instance_types.INSTANCE_TYPES.iteritems()
 | 
			
		||||
            if v['flavorid'] == flavor_id][0]
 | 
			
		||||
 | 
			
		||||
        image_id = env['server']['imageId']
 | 
			
		||||
        
 | 
			
		||||
        img_service, image_id_trans = _image_service()
 | 
			
		||||
 | 
			
		||||
        opaque_image_id = image_id_trans.to_rs_id(image_id)        
 | 
			
		||||
        image = img_service.show(opaque_image_id)
 | 
			
		||||
 | 
			
		||||
        if not image: 
 | 
			
		||||
            raise Exception, "Image not found"
 | 
			
		||||
 | 
			
		||||
        inst['server_name'] = env['server']['name']
 | 
			
		||||
        inst['image_id'] = opaque_image_id
 | 
			
		||||
        inst['user_id'] = user_id
 | 
			
		||||
        inst['launch_time'] = ltime
 | 
			
		||||
        inst['mac_address'] = utils.generate_mac()
 | 
			
		||||
        inst['project_id'] = user_id
 | 
			
		||||
 | 
			
		||||
        inst['project_id'] = env['project']['id']
 | 
			
		||||
        inst['reservation_id'] = reservation
 | 
			
		||||
        reservation = utils.generate_uid('r')
 | 
			
		||||
        inst['state_description'] = 'scheduling'
 | 
			
		||||
        inst['kernel_id'] = image.get('kernelId', FLAGS.default_kernel)
 | 
			
		||||
        inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk)
 | 
			
		||||
        inst['reservation_id'] = utils.generate_uid('r')
 | 
			
		||||
 | 
			
		||||
        address = self.network.allocate_ip(
 | 
			
		||||
                    inst['user_id'],
 | 
			
		||||
                    inst['project_id'],
 | 
			
		||||
                    mac=inst['mac_address'])
 | 
			
		||||
        inst['display_name'] = env['server']['name']
 | 
			
		||||
        inst['display_description'] = env['server']['name']
 | 
			
		||||
 | 
			
		||||
        inst['private_dns_name'] = str(address)
 | 
			
		||||
        inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
 | 
			
		||||
                                inst['user_id'],
 | 
			
		||||
                                inst['project_id'],
 | 
			
		||||
                                'default')['bridge_name']
 | 
			
		||||
        #TODO(dietz) this may be ill advised
 | 
			
		||||
        key_pair_ref = self.db_driver.key_pair_get_all_by_user(
 | 
			
		||||
            None, user_id)[0]
 | 
			
		||||
 | 
			
		||||
        inst['key_data'] = key_pair_ref['public_key']
 | 
			
		||||
        inst['key_name'] = key_pair_ref['name']
 | 
			
		||||
 | 
			
		||||
        #TODO(dietz) stolen from ec2 api, see TODO there
 | 
			
		||||
        inst['security_group'] = 'default'
 | 
			
		||||
 | 
			
		||||
        # Flavor related attributes
 | 
			
		||||
        inst['instance_type'] = instance_type
 | 
			
		||||
        inst['memory_mb'] = flavor['memory_mb']
 | 
			
		||||
        inst['vcpus'] = flavor['vcpus']
 | 
			
		||||
        inst['local_gb'] = flavor['local_gb']
 | 
			
		||||
 | 
			
		||||
        ref = self.db_driver.instance_create(None, inst)
 | 
			
		||||
        inst['id'] = ref.id
 | 
			
		||||
        inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id)
 | 
			
		||||
        
 | 
			
		||||
        # TODO(dietz): this isn't explicitly necessary, but the networking
 | 
			
		||||
        # calls depend on an object with a project_id property, and therefore
 | 
			
		||||
        # should be cleaned up later
 | 
			
		||||
        api_context = context.APIRequestContext(user_id)
 | 
			
		||||
    
 | 
			
		||||
        inst['mac_address'] = utils.generate_mac()
 | 
			
		||||
        
 | 
			
		||||
        #TODO(dietz) is this necessary? 
 | 
			
		||||
        inst['launch_index'] = 0
 | 
			
		||||
 | 
			
		||||
        inst['hostname'] = ref.ec2_id
 | 
			
		||||
        self.db_driver.instance_update(None, inst['id'], inst)
 | 
			
		||||
 | 
			
		||||
        network_manager = utils.import_object(FLAGS.rs_network_manager)
 | 
			
		||||
        address = network_manager.allocate_fixed_ip(api_context,
 | 
			
		||||
            inst['id'])
 | 
			
		||||
 | 
			
		||||
        # TODO(vish): This probably should be done in the scheduler
 | 
			
		||||
        #             network is setup when host is assigned
 | 
			
		||||
        network_topic = self._get_network_topic(user_id)
 | 
			
		||||
        rpc.call(network_topic,
 | 
			
		||||
                 {"method": "setup_fixed_ip",
 | 
			
		||||
                  "args": {"context": None,
 | 
			
		||||
                           "address": address}})
 | 
			
		||||
        return inst
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    def _get_network_topic(self, user_id):
 | 
			
		||||
        """Retrieves the network host for a project"""
 | 
			
		||||
        network_ref = self.db_driver.project_get_network(None, 
 | 
			
		||||
            user_id)
 | 
			
		||||
        host = network_ref['host']
 | 
			
		||||
        if not host:
 | 
			
		||||
            host = rpc.call(FLAGS.network_topic,
 | 
			
		||||
                                  {"method": "set_network_host",
 | 
			
		||||
                                   "args": {"context": None,
 | 
			
		||||
                                            "project_id": user_id}})
 | 
			
		||||
        return self.db_driver.queue_get_for(None, FLAGS.network_topic, host)
 | 
			
		||||
 
 | 
			
		||||
@@ -256,8 +256,7 @@ class LdapDriver(object):
 | 
			
		||||
        if not self.__user_exists(uid):
 | 
			
		||||
            raise exception.NotFound("User %s doesn't exist" % uid)
 | 
			
		||||
        self.__remove_from_all(uid)
 | 
			
		||||
        self.conn.delete_s('uid=%s,%s' % (uid,
 | 
			
		||||
                                          FLAGS.ldap_user_subtree))
 | 
			
		||||
        self.conn.delete_s(self.__uid_to_dn(uid))
 | 
			
		||||
 | 
			
		||||
    def delete_project(self, project_id):
 | 
			
		||||
        """Delete a project"""
 | 
			
		||||
@@ -265,6 +264,19 @@ class LdapDriver(object):
 | 
			
		||||
        self.__delete_roles(project_dn)
 | 
			
		||||
        self.__delete_group(project_dn)
 | 
			
		||||
 | 
			
		||||
    def modify_user(self, uid, access_key=None, secret_key=None, admin=None):
 | 
			
		||||
        """Modify an existing project"""
 | 
			
		||||
        if not access_key and not secret_key and admin is None:
 | 
			
		||||
            return
 | 
			
		||||
        attr = []
 | 
			
		||||
        if access_key:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'accessKey', access_key))
 | 
			
		||||
        if secret_key:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key))
 | 
			
		||||
        if admin is not None:
 | 
			
		||||
            attr.append((self.ldap.MOD_REPLACE, 'isAdmin', str(admin).upper()))
 | 
			
		||||
        self.conn.modify_s(self.__uid_to_dn(uid), attr)
 | 
			
		||||
 | 
			
		||||
    def __user_exists(self, uid):
 | 
			
		||||
        """Check if user exists"""
 | 
			
		||||
        return self.get_user(uid) != None
 | 
			
		||||
 
 | 
			
		||||
@@ -630,6 +630,12 @@ class AuthManager(object):
 | 
			
		||||
        with self.driver() as drv:
 | 
			
		||||
            drv.delete_user(uid)
 | 
			
		||||
 | 
			
		||||
    def modify_user(self, user, access_key=None, secret_key=None, admin=None):
 | 
			
		||||
        """Modify credentials for a user"""
 | 
			
		||||
        uid = User.safe_id(user)
 | 
			
		||||
        with self.driver() as drv:
 | 
			
		||||
            drv.modify_user(uid, access_key, secret_key, admin)
 | 
			
		||||
 | 
			
		||||
    def get_credentials(self, user, project=None):
 | 
			
		||||
        """Get credential zip for user in project"""
 | 
			
		||||
        if not isinstance(user, User):
 | 
			
		||||
 
 | 
			
		||||
@@ -406,9 +406,12 @@ def network_index_count(context):
 | 
			
		||||
    return IMPL.network_index_count(context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def network_index_create(context, values):
 | 
			
		||||
    """Create a network index from the values dict"""
 | 
			
		||||
    return IMPL.network_index_create(context, values)
 | 
			
		||||
def network_index_create_safe(context, values):
 | 
			
		||||
    """Create a network index from the values dict
 | 
			
		||||
 | 
			
		||||
    The index is not returned. If the create violates the unique
 | 
			
		||||
    constraints because the index already exists, no exception is raised."""
 | 
			
		||||
    return IMPL.network_index_create_safe(context, values)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def network_set_cidr(context, network_id, cidr):
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,9 @@ from nova import utils
 | 
			
		||||
from nova.db.sqlalchemy import models
 | 
			
		||||
from nova.db.sqlalchemy.session import get_session
 | 
			
		||||
from sqlalchemy import or_
 | 
			
		||||
from sqlalchemy.exc import IntegrityError
 | 
			
		||||
from sqlalchemy.orm import joinedload_all
 | 
			
		||||
from sqlalchemy.orm.exc import NoResultFound
 | 
			
		||||
from sqlalchemy.sql import exists, func
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
@@ -671,11 +673,14 @@ def network_index_count(_context):
 | 
			
		||||
    return models.NetworkIndex.count()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def network_index_create(_context, values):
 | 
			
		||||
def network_index_create_safe(_context, values):
 | 
			
		||||
    network_index_ref = models.NetworkIndex()
 | 
			
		||||
    for (key, value) in values.iteritems():
 | 
			
		||||
        network_index_ref[key] = value
 | 
			
		||||
    network_index_ref.save()
 | 
			
		||||
    try:
 | 
			
		||||
        network_index_ref.save()
 | 
			
		||||
    except IntegrityError:
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def network_set_host(_context, network_id, host_id):
 | 
			
		||||
 
 | 
			
		||||
@@ -199,6 +199,8 @@ class Instance(BASE, NovaBase):
 | 
			
		||||
    id = Column(Integer, primary_key=True)
 | 
			
		||||
    ec2_id = Column(String(10), unique=True)
 | 
			
		||||
 | 
			
		||||
    admin_pass = Column(String(255))
 | 
			
		||||
 | 
			
		||||
    user_id = Column(String(255))
 | 
			
		||||
    project_id = Column(String(255))
 | 
			
		||||
 | 
			
		||||
@@ -239,7 +241,6 @@ class Instance(BASE, NovaBase):
 | 
			
		||||
    vcpus = Column(Integer)
 | 
			
		||||
    local_gb = Column(Integer)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    hostname = Column(String(255))
 | 
			
		||||
    host = Column(String(255))  # , ForeignKey('hosts.id'))
 | 
			
		||||
 | 
			
		||||
@@ -253,6 +254,10 @@ class Instance(BASE, NovaBase):
 | 
			
		||||
    scheduled_at = Column(DateTime)
 | 
			
		||||
    launched_at = Column(DateTime)
 | 
			
		||||
    terminated_at = Column(DateTime)
 | 
			
		||||
 | 
			
		||||
    display_name = Column(String(255))
 | 
			
		||||
    display_description = Column(String(255))
 | 
			
		||||
 | 
			
		||||
    # TODO(vish): see Ewan's email about state improvements, probably
 | 
			
		||||
    #             should be in a driver base class or some such
 | 
			
		||||
    # vmstate_state = running, halted, suspended, paused
 | 
			
		||||
@@ -289,6 +294,10 @@ class Volume(BASE, NovaBase):
 | 
			
		||||
    launched_at = Column(DateTime)
 | 
			
		||||
    terminated_at = Column(DateTime)
 | 
			
		||||
 | 
			
		||||
    display_name = Column(String(255))
 | 
			
		||||
    display_description = Column(String(255))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Quota(BASE, NovaBase):
 | 
			
		||||
    """Represents quota overrides for a project"""
 | 
			
		||||
    __tablename__ = 'quotas'
 | 
			
		||||
@@ -398,7 +407,7 @@ class NetworkIndex(BASE, NovaBase):
 | 
			
		||||
    """
 | 
			
		||||
    __tablename__ = 'network_indexes'
 | 
			
		||||
    id = Column(Integer, primary_key=True)
 | 
			
		||||
    index = Column(Integer)
 | 
			
		||||
    index = Column(Integer, unique=True)
 | 
			
		||||
    network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
 | 
			
		||||
    network = relationship(Network, backref=backref('network_index',
 | 
			
		||||
                                                    uselist=False))
 | 
			
		||||
 
 | 
			
		||||
@@ -188,6 +188,8 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid')
 | 
			
		||||
DEFINE_string('rabbit_password', 'guest', 'rabbit password')
 | 
			
		||||
DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
 | 
			
		||||
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
 | 
			
		||||
DEFINE_string('cc_host', '127.0.0.1', 'ip of api server')
 | 
			
		||||
DEFINE_integer('cc_port', 8773, 'cloud controller port')
 | 
			
		||||
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud',
 | 
			
		||||
              'Url to ec2 api server')
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,3 +43,10 @@ class Manager(object):
 | 
			
		||||
    def periodic_tasks(self, context=None):
 | 
			
		||||
        """Tasks to be run at a periodic interval"""
 | 
			
		||||
        yield
 | 
			
		||||
 | 
			
		||||
    def init_host(self):
 | 
			
		||||
       """Do any initialization that needs to be run if this is a standalone service.
 | 
			
		||||
 | 
			
		||||
       Child classes should override this method.
 | 
			
		||||
       """
 | 
			
		||||
       pass
 | 
			
		||||
 
 | 
			
		||||
@@ -36,13 +36,34 @@ flags.DEFINE_string('dhcpbridge_flagfile',
 | 
			
		||||
flags.DEFINE_string('networks_path', utils.abspath('../networks'),
 | 
			
		||||
                    'Location to keep network config files')
 | 
			
		||||
flags.DEFINE_string('public_interface', 'vlan1',
 | 
			
		||||
                        'Interface for public IP addresses')
 | 
			
		||||
                    'Interface for public IP addresses')
 | 
			
		||||
flags.DEFINE_string('bridge_dev', 'eth0',
 | 
			
		||||
                        'network device for bridges')
 | 
			
		||||
 | 
			
		||||
                    'network device for bridges')
 | 
			
		||||
flags.DEFINE_string('routing_source_ip', '127.0.0.1',
 | 
			
		||||
                    'Public IP of network host')
 | 
			
		||||
flags.DEFINE_bool('use_nova_chains', False,
 | 
			
		||||
                  'use the nova_ routing chains instead of default')
 | 
			
		||||
 | 
			
		||||
DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)]
 | 
			
		||||
 | 
			
		||||
def init_host():
 | 
			
		||||
    """Basic networking setup goes here"""
 | 
			
		||||
    # NOTE(devcamcar): Cloud public DNAT entries, CloudPipe port
 | 
			
		||||
    # forwarding entries and a default DNAT entry.
 | 
			
		||||
    _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 "
 | 
			
		||||
             "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT "
 | 
			
		||||
             "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port))
 | 
			
		||||
 | 
			
		||||
    # NOTE(devcamcar): Cloud public SNAT entries and the default
 | 
			
		||||
    # SNAT rule for outbound traffic.
 | 
			
		||||
    _confirm_rule("POSTROUTING", "-t nat -s %s "
 | 
			
		||||
             "-j SNAT --to-source %s"
 | 
			
		||||
             % (FLAGS.private_range, FLAGS.routing_source_ip))
 | 
			
		||||
 | 
			
		||||
    _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" %
 | 
			
		||||
                  FLAGS.private_range)
 | 
			
		||||
    _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" %
 | 
			
		||||
                  {'range': FLAGS.private_range})
 | 
			
		||||
 | 
			
		||||
def bind_floating_ip(floating_ip):
 | 
			
		||||
    """Bind ip to public interface"""
 | 
			
		||||
@@ -58,37 +79,37 @@ def unbind_floating_ip(floating_ip):
 | 
			
		||||
 | 
			
		||||
def ensure_vlan_forward(public_ip, port, private_ip):
 | 
			
		||||
    """Sets up forwarding rules for vlan"""
 | 
			
		||||
    _confirm_rule("FORWARD -d %s -p udp --dport 1194 -j ACCEPT" % private_ip)
 | 
			
		||||
    _confirm_rule(
 | 
			
		||||
        "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194"
 | 
			
		||||
    _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" %
 | 
			
		||||
                  private_ip)
 | 
			
		||||
    _confirm_rule("PREROUTING",
 | 
			
		||||
                  "-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194"
 | 
			
		||||
            % (public_ip, port, private_ip))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ensure_floating_forward(floating_ip, fixed_ip):
 | 
			
		||||
    """Ensure floating ip forwarding rule"""
 | 
			
		||||
    _confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s"
 | 
			
		||||
    _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s"
 | 
			
		||||
                           % (floating_ip, fixed_ip))
 | 
			
		||||
    _confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s"
 | 
			
		||||
    _confirm_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s"
 | 
			
		||||
                           % (fixed_ip, floating_ip))
 | 
			
		||||
    # TODO(joshua): Get these from the secgroup datastore entries
 | 
			
		||||
    _confirm_rule("FORWARD -d %s -p icmp -j ACCEPT"
 | 
			
		||||
    _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT"
 | 
			
		||||
                           % (fixed_ip))
 | 
			
		||||
    for (protocol, port) in DEFAULT_PORTS:
 | 
			
		||||
        _confirm_rule(
 | 
			
		||||
            "FORWARD -d %s -p %s --dport %s -j ACCEPT"
 | 
			
		||||
        _confirm_rule("FORWARD","-d %s -p %s --dport %s -j ACCEPT"
 | 
			
		||||
            % (fixed_ip, protocol, port))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def remove_floating_forward(floating_ip, fixed_ip):
 | 
			
		||||
    """Remove forwarding for floating ip"""
 | 
			
		||||
    _remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s"
 | 
			
		||||
    _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s"
 | 
			
		||||
                          % (floating_ip, fixed_ip))
 | 
			
		||||
    _remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s"
 | 
			
		||||
    _remove_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s"
 | 
			
		||||
                          % (fixed_ip, floating_ip))
 | 
			
		||||
    _remove_rule("FORWARD -d %s -p icmp -j ACCEPT"
 | 
			
		||||
    _remove_rule("FORWARD", "-d %s -p icmp -j ACCEPT"
 | 
			
		||||
                          % (fixed_ip))
 | 
			
		||||
    for (protocol, port) in DEFAULT_PORTS:
 | 
			
		||||
        _remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT"
 | 
			
		||||
        _remove_rule("FORWARD", "-d %s -p %s --dport %s -j ACCEPT"
 | 
			
		||||
                              % (fixed_ip, protocol, port))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -124,9 +145,10 @@ def ensure_bridge(bridge, interface, net_attrs=None):
 | 
			
		||||
                     net_attrs['gateway'],
 | 
			
		||||
                     net_attrs['broadcast'],
 | 
			
		||||
                     net_attrs['netmask']))
 | 
			
		||||
            _confirm_rule("FORWARD --in-interface %s -j ACCEPT" % bridge)
 | 
			
		||||
        else:
 | 
			
		||||
            _execute("sudo ifconfig %s up" % bridge)
 | 
			
		||||
        _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge)
 | 
			
		||||
        _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_dhcp_hosts(context, network_id):
 | 
			
		||||
@@ -195,15 +217,19 @@ def _device_exists(device):
 | 
			
		||||
    return not err
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _confirm_rule(cmd):
 | 
			
		||||
def _confirm_rule(chain, cmd):
 | 
			
		||||
    """Delete and re-add iptables rule"""
 | 
			
		||||
    _execute("sudo iptables --delete %s" % (cmd), check_exit_code=False)
 | 
			
		||||
    _execute("sudo iptables -I %s" % (cmd))
 | 
			
		||||
    if FLAGS.use_nova_chains:
 | 
			
		||||
        chain = "nova_%s" % chain.lower()
 | 
			
		||||
    _execute("sudo iptables --delete %s %s" % (chain, cmd), check_exit_code=False)
 | 
			
		||||
    _execute("sudo iptables -I %s %s" % (chain, cmd))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _remove_rule(cmd):
 | 
			
		||||
def _remove_rule(chain, cmd):
 | 
			
		||||
    """Remove iptables rule"""
 | 
			
		||||
    _execute("sudo iptables --delete %s" % (cmd))
 | 
			
		||||
    if FLAGS.use_nova_chains:
 | 
			
		||||
        chain = "%S" % chain.lower()
 | 
			
		||||
    _execute("sudo iptables --delete %s %s" % (chain, cmd))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _dnsmasq_cmd(net):
 | 
			
		||||
 
 | 
			
		||||
@@ -236,6 +236,11 @@ class VlanManager(NetworkManager):
 | 
			
		||||
        if num:
 | 
			
		||||
            logging.debug("Dissassociated %s stale fixed ip(s)", num)
 | 
			
		||||
 | 
			
		||||
    def init_host(self):
 | 
			
		||||
        """Do any initialization that needs to be run if this is a
 | 
			
		||||
           standalone service.
 | 
			
		||||
        """
 | 
			
		||||
        self.driver.init_host()
 | 
			
		||||
 | 
			
		||||
    def allocate_fixed_ip(self, context, instance_id, *args, **kwargs):
 | 
			
		||||
        """Gets a fixed ip from the pool"""
 | 
			
		||||
@@ -354,7 +359,7 @@ class VlanManager(NetworkManager):
 | 
			
		||||
        This could use a manage command instead of keying off of a flag"""
 | 
			
		||||
        if not self.db.network_index_count(context):
 | 
			
		||||
            for index in range(FLAGS.num_networks):
 | 
			
		||||
                self.db.network_index_create(context, {'index': index})
 | 
			
		||||
                self.db.network_index_create_safe(context, {'index': index})
 | 
			
		||||
 | 
			
		||||
    def _on_set_network_host(self, context, network_id):
 | 
			
		||||
        """Called when this host becomes the host for a project"""
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
.. automodule:: nova.objectstore
 | 
			
		||||
   :platform: Unix
 | 
			
		||||
   :synopsis: Currently a trivial file-based system, getting extended w/ mongo.
 | 
			
		||||
   :synopsis: Currently a trivial file-based system, getting extended w/ swift.
 | 
			
		||||
.. moduleauthor:: Jesse Andrews <jesse@ansolabs.com>
 | 
			
		||||
.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
 | 
			
		||||
.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
 | 
			
		||||
 
 | 
			
		||||
@@ -352,6 +352,8 @@ class ImagesResource(resource.Resource):
 | 
			
		||||
                m[u'imageType'] = m['type']
 | 
			
		||||
            elif 'imageType' in m:
 | 
			
		||||
                m[u'type'] = m['imageType']
 | 
			
		||||
            if 'displayName' not in m:
 | 
			
		||||
                m[u'displayName'] = u''
 | 
			
		||||
            return m
 | 
			
		||||
 | 
			
		||||
        request.write(json.dumps([decorate(i.metadata) for i in images]))
 | 
			
		||||
@@ -382,16 +384,25 @@ class ImagesResource(resource.Resource):
 | 
			
		||||
    def render_POST(self, request): # pylint: disable-msg=R0201
 | 
			
		||||
        """Update image attributes: public/private"""
 | 
			
		||||
 | 
			
		||||
        # image_id required for all requests
 | 
			
		||||
        image_id = get_argument(request, 'image_id', u'')
 | 
			
		||||
        operation = get_argument(request, 'operation', u'')
 | 
			
		||||
 | 
			
		||||
        image_object = image.Image(image_id)
 | 
			
		||||
 | 
			
		||||
        if not image_object.is_authorized(request.context):
 | 
			
		||||
            logging.debug("not authorized for render_POST in images")
 | 
			
		||||
            raise exception.NotAuthorized
 | 
			
		||||
 | 
			
		||||
        image_object.set_public(operation=='add')
 | 
			
		||||
 | 
			
		||||
        operation = get_argument(request, 'operation', u'')
 | 
			
		||||
        if operation:
 | 
			
		||||
            # operation implies publicity toggle
 | 
			
		||||
            logging.debug("handling publicity toggle")
 | 
			
		||||
            image_object.set_public(operation=='add')
 | 
			
		||||
        else:
 | 
			
		||||
            # other attributes imply update
 | 
			
		||||
            logging.debug("update user fields")
 | 
			
		||||
            clean_args = {}
 | 
			
		||||
            for arg in request.args.keys():
 | 
			
		||||
                clean_args[arg] = request.args[arg][0]
 | 
			
		||||
            image_object.update_user_editable_fields(clean_args)
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def render_DELETE(self, request): # pylint: disable-msg=R0201
 | 
			
		||||
 
 | 
			
		||||
@@ -82,6 +82,16 @@ class Image(object):
 | 
			
		||||
        with open(os.path.join(self.path, 'info.json'), 'w') as f:
 | 
			
		||||
            json.dump(md, f)
 | 
			
		||||
 | 
			
		||||
    def update_user_editable_fields(self, args):
 | 
			
		||||
        """args is from the request parameters, so requires extra cleaning"""
 | 
			
		||||
        fields = {'display_name': 'displayName', 'description': 'description'}
 | 
			
		||||
        info = self.metadata
 | 
			
		||||
        for field in fields.keys():
 | 
			
		||||
            if field in args:
 | 
			
		||||
                info[fields[field]] = args[field]
 | 
			
		||||
        with open(os.path.join(self.path, 'info.json'), 'w') as f:
 | 
			
		||||
            json.dump(info, f)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def all():
 | 
			
		||||
        images = []
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,7 @@ class Service(object, service.Service):
 | 
			
		||||
        self.topic = topic
 | 
			
		||||
        manager_class = utils.import_class(manager)
 | 
			
		||||
        self.manager = manager_class(host=host, *args, **kwargs)
 | 
			
		||||
        self.manager.init_host()
 | 
			
		||||
        self.model_disconnected = False
 | 
			
		||||
        super(Service, self).__init__(*args, **kwargs)
 | 
			
		||||
        try:
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from nova.api.rackspace import limited
 | 
			
		||||
from nova.api.rackspace import RateLimitingMiddleware
 | 
			
		||||
from nova.tests.api.test_helper import *
 | 
			
		||||
from webob import Request
 | 
			
		||||
@@ -77,3 +78,31 @@ class RateLimitingMiddlewareTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(middleware.limiter.__class__.__name__, "Limiter")
 | 
			
		||||
        middleware = RateLimitingMiddleware(APIStub(), service_host='foobar')
 | 
			
		||||
        self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LimiterTest(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def testLimiter(self):
 | 
			
		||||
        items = range(2000)
 | 
			
		||||
        req = Request.blank('/')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[ :1000])
 | 
			
		||||
        req = Request.blank('/?offset=0')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[ :1000])
 | 
			
		||||
        req = Request.blank('/?offset=3')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[3:1003])
 | 
			
		||||
        req = Request.blank('/?offset=2005')
 | 
			
		||||
        self.assertEqual(limited(items, req), [])
 | 
			
		||||
        req = Request.blank('/?limit=10')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[ :10])
 | 
			
		||||
        req = Request.blank('/?limit=0')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[ :1000])
 | 
			
		||||
        req = Request.blank('/?limit=3000')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[ :1000])
 | 
			
		||||
        req = Request.blank('/?offset=1&limit=3')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[1:4])
 | 
			
		||||
        req = Request.blank('/?offset=3&limit=0')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[3:1003])
 | 
			
		||||
        req = Request.blank('/?offset=3&limit=1500')
 | 
			
		||||
        self.assertEqual(limited(items, req), items[3:1003])
 | 
			
		||||
        req = Request.blank('/?offset=3000&limit=10')
 | 
			
		||||
        self.assertEqual(limited(items, req), [])
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,14 @@
 | 
			
		||||
import datetime
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
import stubout
 | 
			
		||||
import webob
 | 
			
		||||
import webob.dec
 | 
			
		||||
import unittest
 | 
			
		||||
import stubout
 | 
			
		||||
 | 
			
		||||
import nova.api
 | 
			
		||||
import nova.api.rackspace.auth
 | 
			
		||||
from nova import auth
 | 
			
		||||
from nova.tests.api.rackspace import test_helper
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
class Test(unittest.TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,6 @@ class FlavorsTest(unittest.TestCase):
 | 
			
		||||
    def test_get_flavor_list(self):
 | 
			
		||||
        req = webob.Request.blank('/v1.0/flavors')
 | 
			
		||||
        res = req.get_response(nova.api.API())
 | 
			
		||||
        print res
 | 
			
		||||
 | 
			
		||||
    def test_get_flavor_by_id(self):
 | 
			
		||||
        pass
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import stubout
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from nova.api.rackspace import images
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ import nova.api.rackspace
 | 
			
		||||
from nova.api.rackspace import servers
 | 
			
		||||
import nova.db.api
 | 
			
		||||
from nova.db.sqlalchemy.models import Instance
 | 
			
		||||
import nova.rpc
 | 
			
		||||
from nova.tests.api.test_helper import *
 | 
			
		||||
from nova.tests.api.rackspace import test_helper
 | 
			
		||||
 | 
			
		||||
@@ -52,8 +53,11 @@ class ServersTest(unittest.TestCase):
 | 
			
		||||
        test_helper.stub_for_testing(self.stubs)
 | 
			
		||||
        test_helper.stub_out_rate_limiting(self.stubs)
 | 
			
		||||
        test_helper.stub_out_auth(self.stubs)
 | 
			
		||||
        test_helper.stub_out_id_translator(self.stubs)
 | 
			
		||||
        test_helper.stub_out_key_pair_funcs(self.stubs)
 | 
			
		||||
        test_helper.stub_out_image_service(self.stubs)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_get_all', return_servers)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_get', return_server)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_get_by_ec2_id', return_server)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_get_all_by_user', 
 | 
			
		||||
            return_servers)
 | 
			
		||||
 | 
			
		||||
@@ -67,9 +71,6 @@ class ServersTest(unittest.TestCase):
 | 
			
		||||
        self.assertEqual(res_dict['server']['id'], '1')
 | 
			
		||||
        self.assertEqual(res_dict['server']['name'], 'server1')
 | 
			
		||||
 | 
			
		||||
    def test_get_backup_schedule(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def test_get_server_list(self):
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers')
 | 
			
		||||
        res = req.get_response(nova.api.API())
 | 
			
		||||
@@ -82,24 +83,86 @@ class ServersTest(unittest.TestCase):
 | 
			
		||||
            self.assertEqual(s.get('imageId', None), None)
 | 
			
		||||
            i += 1
 | 
			
		||||
 | 
			
		||||
    #def test_create_instance(self):
 | 
			
		||||
    #    test_helper.stub_out_image_translator(self.stubs)
 | 
			
		||||
    #    body = dict(server=dict(
 | 
			
		||||
    #        name='server_test', imageId=2, flavorId=2, metadata={},
 | 
			
		||||
    #        personality = {}
 | 
			
		||||
    #    ))
 | 
			
		||||
    #    req = webob.Request.blank('/v1.0/servers')
 | 
			
		||||
    #    req.method = 'POST'
 | 
			
		||||
    #    req.body = json.dumps(body)
 | 
			
		||||
    def test_create_instance(self):
 | 
			
		||||
        def server_update(context, id, params):
 | 
			
		||||
            pass 
 | 
			
		||||
 | 
			
		||||
    #    res = req.get_response(nova.api.API())
 | 
			
		||||
        def instance_create(context, inst):
 | 
			
		||||
            class Foo(object):
 | 
			
		||||
                ec2_id = 1
 | 
			
		||||
            return Foo()
 | 
			
		||||
 | 
			
		||||
    #    print res
 | 
			
		||||
    def test_update_server_password(self):
 | 
			
		||||
        pass
 | 
			
		||||
        def fake_method(*args, **kwargs):
 | 
			
		||||
            pass
 | 
			
		||||
        
 | 
			
		||||
    def test_update_server_name(self):
 | 
			
		||||
        pass
 | 
			
		||||
        def project_get_network(context, user_id):
 | 
			
		||||
            return dict(id='1', host='localhost') 
 | 
			
		||||
 | 
			
		||||
        def queue_get_for(context, *args):
 | 
			
		||||
            return 'network_topic'
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'project_get_network', project_get_network)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_create', instance_create)
 | 
			
		||||
        self.stubs.Set(nova.rpc, 'cast', fake_method)
 | 
			
		||||
        self.stubs.Set(nova.rpc, 'call', fake_method)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_update',
 | 
			
		||||
            server_update)
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for)
 | 
			
		||||
        self.stubs.Set(nova.network.manager.FlatManager, 'allocate_fixed_ip',
 | 
			
		||||
            fake_method)
 | 
			
		||||
            
 | 
			
		||||
        test_helper.stub_out_id_translator(self.stubs)
 | 
			
		||||
        body = dict(server=dict(
 | 
			
		||||
            name='server_test', imageId=2, flavorId=2, metadata={},
 | 
			
		||||
            personality = {}
 | 
			
		||||
        ))
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers')
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
        req.body = json.dumps(body)
 | 
			
		||||
 | 
			
		||||
        res = req.get_response(nova.api.API())
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(res.status_int, 200)
 | 
			
		||||
 | 
			
		||||
    def test_update_no_body(self):
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers/1')
 | 
			
		||||
        req.method = 'PUT'
 | 
			
		||||
        res = req.get_response(nova.api.API())
 | 
			
		||||
        self.assertEqual(res.status_int, 422)
 | 
			
		||||
 | 
			
		||||
    def test_update_bad_params(self):
 | 
			
		||||
        """ Confirm that update is filtering params """
 | 
			
		||||
        inst_dict = dict(cat='leopard', name='server_test', adminPass='bacon')
 | 
			
		||||
        self.body = json.dumps(dict(server=inst_dict))
 | 
			
		||||
 | 
			
		||||
        def server_update(context, id, params):
 | 
			
		||||
            self.update_called = True
 | 
			
		||||
            filtered_dict = dict(name='server_test', admin_pass='bacon')
 | 
			
		||||
            self.assertEqual(params, filtered_dict)
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_update',
 | 
			
		||||
            server_update)
 | 
			
		||||
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers/1')
 | 
			
		||||
        req.method = 'PUT'
 | 
			
		||||
        req.body = self.body
 | 
			
		||||
        req.get_response(nova.api.API())
 | 
			
		||||
 | 
			
		||||
    def test_update_server(self):
 | 
			
		||||
        inst_dict = dict(name='server_test', adminPass='bacon')
 | 
			
		||||
        self.body = json.dumps(dict(server=inst_dict))
 | 
			
		||||
 | 
			
		||||
        def server_update(context, id, params):
 | 
			
		||||
            filtered_dict = dict(name='server_test', admin_pass='bacon')
 | 
			
		||||
            self.assertEqual(params, filtered_dict)
 | 
			
		||||
 | 
			
		||||
        self.stubs.Set(nova.db.api, 'instance_update',
 | 
			
		||||
            server_update)
 | 
			
		||||
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers/1')
 | 
			
		||||
        req.method = 'PUT'
 | 
			
		||||
        req.body = self.body
 | 
			
		||||
        req.get_response(nova.api.API())
 | 
			
		||||
 | 
			
		||||
    def test_create_backup_schedules(self):
 | 
			
		||||
        req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import stubout
 | 
			
		||||
import unittest
 | 
			
		||||
 | 
			
		||||
from nova.api.rackspace import sharedipgroups
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ from nova import utils
 | 
			
		||||
from nova import flags
 | 
			
		||||
import nova.api.rackspace.auth
 | 
			
		||||
import nova.api.rackspace._id_translator
 | 
			
		||||
from nova.image import service
 | 
			
		||||
from nova.wsgi import Router
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
@@ -40,7 +41,19 @@ def fake_wsgi(self, req):
 | 
			
		||||
        req.environ['inst_dict'] = json.loads(req.body)
 | 
			
		||||
    return self.application
 | 
			
		||||
 | 
			
		||||
def stub_out_image_translator(stubs):
 | 
			
		||||
def stub_out_key_pair_funcs(stubs):
 | 
			
		||||
    def key_pair(context, user_id):
 | 
			
		||||
        return [dict(name='key', public_key='public_key')]
 | 
			
		||||
    stubs.Set(nova.db.api, 'key_pair_get_all_by_user',
 | 
			
		||||
        key_pair)
 | 
			
		||||
 | 
			
		||||
def stub_out_image_service(stubs):
 | 
			
		||||
    def fake_image_show(meh, id):
 | 
			
		||||
        return dict(kernelId=1, ramdiskId=1)
 | 
			
		||||
 | 
			
		||||
    stubs.Set(nova.image.service.LocalImageService, 'show', fake_image_show)
 | 
			
		||||
 | 
			
		||||
def stub_out_id_translator(stubs):
 | 
			
		||||
    class FakeTranslator(object):
 | 
			
		||||
        def __init__(self, id_type, service_name):
 | 
			
		||||
            pass
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										40
									
								
								nova/tests/api/rackspace/testfaults.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								nova/tests/api/rackspace/testfaults.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
import unittest
 | 
			
		||||
import webob
 | 
			
		||||
import webob.dec
 | 
			
		||||
import webob.exc
 | 
			
		||||
 | 
			
		||||
from nova.api.rackspace import faults
 | 
			
		||||
 | 
			
		||||
class TestFaults(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_fault_parts(self):
 | 
			
		||||
        req = webob.Request.blank('/.xml')
 | 
			
		||||
        f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram'))
 | 
			
		||||
        resp = req.get_response(f)
 | 
			
		||||
 | 
			
		||||
        first_two_words = resp.body.strip().split()[:2]
 | 
			
		||||
        self.assertEqual(first_two_words, ['<badRequest', 'code="400">'])
 | 
			
		||||
        body_without_spaces = ''.join(resp.body.split())
 | 
			
		||||
        self.assertTrue('<message>scram</message>' in body_without_spaces)
 | 
			
		||||
 | 
			
		||||
    def test_retry_header(self):
 | 
			
		||||
        req = webob.Request.blank('/.xml')
 | 
			
		||||
        exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry', 
 | 
			
		||||
                                                  headers={'Retry-After': 4})
 | 
			
		||||
        f = faults.Fault(exc)
 | 
			
		||||
        resp = req.get_response(f)
 | 
			
		||||
        first_two_words = resp.body.strip().split()[:2]
 | 
			
		||||
        self.assertEqual(first_two_words, ['<overLimit', 'code="413">'])
 | 
			
		||||
        body_sans_spaces = ''.join(resp.body.split())
 | 
			
		||||
        self.assertTrue('<message>sorry</message>' in body_sans_spaces)
 | 
			
		||||
        self.assertTrue('<retryAfter>4</retryAfter>' in body_sans_spaces)
 | 
			
		||||
        self.assertEqual(resp.headers['Retry-After'], 4)
 | 
			
		||||
 | 
			
		||||
    def test_raise(self):
 | 
			
		||||
        @webob.dec.wsgify
 | 
			
		||||
        def raiser(req):
 | 
			
		||||
            raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?'))
 | 
			
		||||
        req = webob.Request.blank('/.xml')
 | 
			
		||||
        resp = req.get_response(raiser)
 | 
			
		||||
        self.assertEqual(resp.status_int, 404)
 | 
			
		||||
        self.assertTrue('whut?' in resp.body)
 | 
			
		||||
@@ -28,26 +28,71 @@ from nova.api.ec2 import cloud
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
class user_generator(object):
 | 
			
		||||
    def __init__(self, manager, **user_state):
 | 
			
		||||
        if 'name' not in user_state:
 | 
			
		||||
            user_state['name'] = 'test1'
 | 
			
		||||
        self.manager = manager
 | 
			
		||||
        self.user = manager.create_user(**user_state)
 | 
			
		||||
 | 
			
		||||
class AuthTestCase(test.TrialTestCase):
 | 
			
		||||
    flush_db = False
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        return self.user
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, value, type, trace):
 | 
			
		||||
        self.manager.delete_user(self.user)
 | 
			
		||||
 | 
			
		||||
class project_generator(object):
 | 
			
		||||
    def __init__(self, manager, **project_state):
 | 
			
		||||
        if 'name' not in project_state:
 | 
			
		||||
            project_state['name'] = 'testproj'
 | 
			
		||||
        if 'manager_user' not in project_state:
 | 
			
		||||
            project_state['manager_user'] = 'test1'
 | 
			
		||||
        self.manager = manager
 | 
			
		||||
        self.project = manager.create_project(**project_state)
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        return self.project
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, value, type, trace):
 | 
			
		||||
        self.manager.delete_project(self.project)
 | 
			
		||||
 | 
			
		||||
class user_and_project_generator(object):
 | 
			
		||||
    def __init__(self, manager, user_state={}, project_state={}):
 | 
			
		||||
        self.manager = manager
 | 
			
		||||
        if 'name' not in user_state:
 | 
			
		||||
            user_state['name'] = 'test1'
 | 
			
		||||
        if 'name' not in project_state:
 | 
			
		||||
            project_state['name'] = 'testproj'
 | 
			
		||||
        if 'manager_user' not in project_state:
 | 
			
		||||
            project_state['manager_user'] = 'test1'
 | 
			
		||||
        self.user = manager.create_user(**user_state)
 | 
			
		||||
        self.project = manager.create_project(**project_state)
 | 
			
		||||
 | 
			
		||||
    def __enter__(self):
 | 
			
		||||
        return (self.user, self.project)
 | 
			
		||||
 | 
			
		||||
    def __exit__(self, value, type, trace):
 | 
			
		||||
        self.manager.delete_user(self.user)
 | 
			
		||||
        self.manager.delete_project(self.project)
 | 
			
		||||
 | 
			
		||||
class AuthManagerTestCase(test.TrialTestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(AuthTestCase, self).setUp()
 | 
			
		||||
        super(AuthManagerTestCase, self).setUp()
 | 
			
		||||
        self.flags(connection_type='fake')
 | 
			
		||||
        self.manager = manager.AuthManager()
 | 
			
		||||
 | 
			
		||||
    def test_001_can_create_users(self):
 | 
			
		||||
        self.manager.create_user('test1', 'access', 'secret')
 | 
			
		||||
        self.manager.create_user('test2')
 | 
			
		||||
    def test_create_and_find_user(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            self.assert_(self.manager.get_user('test1'))
 | 
			
		||||
 | 
			
		||||
    def test_002_can_get_user(self):
 | 
			
		||||
        user = self.manager.get_user('test1')
 | 
			
		||||
 | 
			
		||||
    def test_003_can_retreive_properties(self):
 | 
			
		||||
        user = self.manager.get_user('test1')
 | 
			
		||||
        self.assertEqual('test1', user.id)
 | 
			
		||||
        self.assertEqual('access', user.access)
 | 
			
		||||
        self.assertEqual('secret', user.secret)
 | 
			
		||||
    def test_create_and_find_with_properties(self):
 | 
			
		||||
        with user_generator(self.manager, name="herbert", secret="classified",
 | 
			
		||||
                        access="private-party"):
 | 
			
		||||
            u = self.manager.get_user('herbert')
 | 
			
		||||
            self.assertEqual('herbert', u.id)
 | 
			
		||||
            self.assertEqual('herbert', u.name)
 | 
			
		||||
            self.assertEqual('classified', u.secret)
 | 
			
		||||
            self.assertEqual('private-party', u.access)
 | 
			
		||||
 | 
			
		||||
    def test_004_signature_is_valid(self):
 | 
			
		||||
        #self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? ))
 | 
			
		||||
@@ -64,133 +109,216 @@ class AuthTestCase(test.TrialTestCase):
 | 
			
		||||
        'export S3_URL="http://127.0.0.1:3333/"\n' +
 | 
			
		||||
        'export EC2_USER_ID="test1"\n')
 | 
			
		||||
 | 
			
		||||
    def test_010_can_list_users(self):
 | 
			
		||||
        users = self.manager.get_users()
 | 
			
		||||
        logging.warn(users)
 | 
			
		||||
        self.assertTrue(filter(lambda u: u.id == 'test1', users))
 | 
			
		||||
    def test_can_list_users(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            with user_generator(self.manager, name="test2"):
 | 
			
		||||
                users = self.manager.get_users()
 | 
			
		||||
                self.assert_(filter(lambda u: u.id == 'test1', users))
 | 
			
		||||
                self.assert_(filter(lambda u: u.id == 'test2', users))
 | 
			
		||||
                self.assert_(not filter(lambda u: u.id == 'test3', users))
 | 
			
		||||
        
 | 
			
		||||
    def test_101_can_add_user_role(self):
 | 
			
		||||
        self.assertFalse(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
        self.manager.add_role('test1', 'itsec')
 | 
			
		||||
        self.assertTrue(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
    def test_can_add_and_remove_user_role(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            self.assertFalse(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
            self.manager.add_role('test1', 'itsec')
 | 
			
		||||
            self.assertTrue(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
            self.manager.remove_role('test1', 'itsec')
 | 
			
		||||
            self.assertFalse(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
 | 
			
		||||
    def test_199_can_remove_user_role(self):
 | 
			
		||||
        self.assertTrue(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
        self.manager.remove_role('test1', 'itsec')
 | 
			
		||||
        self.assertFalse(self.manager.has_role('test1', 'itsec'))
 | 
			
		||||
    def test_can_create_and_get_project(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (u,p):
 | 
			
		||||
            self.assert_(self.manager.get_user('test1'))
 | 
			
		||||
            self.assert_(self.manager.get_user('test1'))
 | 
			
		||||
            self.assert_(self.manager.get_project('testproj'))
 | 
			
		||||
 | 
			
		||||
    def test_201_can_create_project(self):
 | 
			
		||||
        project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1'])
 | 
			
		||||
        self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
 | 
			
		||||
        self.assertEqual(project.name, 'testproj')
 | 
			
		||||
        self.assertEqual(project.description, 'A test project')
 | 
			
		||||
        self.assertEqual(project.project_manager_id, 'test1')
 | 
			
		||||
        self.assertTrue(project.has_member('test1'))
 | 
			
		||||
    def test_can_list_projects(self):
 | 
			
		||||
        with user_and_project_generator(self.manager):
 | 
			
		||||
            with project_generator(self.manager, name="testproj2"):
 | 
			
		||||
                projects = self.manager.get_projects()
 | 
			
		||||
                self.assert_(filter(lambda p: p.name == 'testproj', projects))
 | 
			
		||||
                self.assert_(filter(lambda p: p.name == 'testproj2', projects))
 | 
			
		||||
                self.assert_(not filter(lambda p: p.name == 'testproj3',
 | 
			
		||||
                                        projects))
 | 
			
		||||
 | 
			
		||||
    def test_202_user1_is_project_member(self):
 | 
			
		||||
        self.assertTrue(self.manager.get_user('test1').is_project_member('testproj'))
 | 
			
		||||
    def test_can_create_and_get_project_with_attributes(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            with project_generator(self.manager, description='A test project'):
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertEqual('A test project', project.description)
 | 
			
		||||
 | 
			
		||||
    def test_203_user2_is_not_project_member(self):
 | 
			
		||||
        self.assertFalse(self.manager.get_user('test2').is_project_member('testproj'))
 | 
			
		||||
    def test_can_create_project_with_manager(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.assertEqual('test1', project.project_manager_id)
 | 
			
		||||
            self.assertTrue(self.manager.is_project_manager(user, project))
 | 
			
		||||
 | 
			
		||||
    def test_204_user1_is_project_manager(self):
 | 
			
		||||
        self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj'))
 | 
			
		||||
    def test_create_project_assigns_manager_to_members(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.assertTrue(self.manager.is_project_member(user, project))
 | 
			
		||||
 | 
			
		||||
    def test_205_user2_is_not_project_manager(self):
 | 
			
		||||
        self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj'))
 | 
			
		||||
    def test_no_extra_project_members(self):
 | 
			
		||||
        with user_generator(self.manager, name='test2') as baduser:
 | 
			
		||||
            with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
                self.assertFalse(self.manager.is_project_member(baduser,
 | 
			
		||||
                                                                 project))
 | 
			
		||||
 | 
			
		||||
    def test_206_can_add_user_to_project(self):
 | 
			
		||||
        self.manager.add_to_project('test2', 'testproj')
 | 
			
		||||
        self.assertTrue(self.manager.get_project('testproj').has_member('test2'))
 | 
			
		||||
    def test_no_extra_project_managers(self):
 | 
			
		||||
        with user_generator(self.manager, name='test2') as baduser:
 | 
			
		||||
            with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
                self.assertFalse(self.manager.is_project_manager(baduser,
 | 
			
		||||
                                                                 project))
 | 
			
		||||
 | 
			
		||||
    def test_207_can_remove_user_from_project(self):
 | 
			
		||||
        self.manager.remove_from_project('test2', 'testproj')
 | 
			
		||||
        self.assertFalse(self.manager.get_project('testproj').has_member('test2'))
 | 
			
		||||
    def test_can_add_user_to_project(self):
 | 
			
		||||
        with user_generator(self.manager, name='test2') as user:
 | 
			
		||||
            with user_and_project_generator(self.manager) as (_user, project):
 | 
			
		||||
                self.manager.add_to_project(user, project)
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertTrue(self.manager.is_project_member(user, project))
 | 
			
		||||
 | 
			
		||||
    def test_208_can_remove_add_user_with_role(self):
 | 
			
		||||
        self.manager.add_to_project('test2', 'testproj')
 | 
			
		||||
        self.manager.add_role('test2', 'developer', 'testproj')
 | 
			
		||||
        self.manager.remove_from_project('test2', 'testproj')
 | 
			
		||||
        self.assertFalse(self.manager.has_role('test2', 'developer', 'testproj'))
 | 
			
		||||
        self.manager.add_to_project('test2', 'testproj')
 | 
			
		||||
        self.manager.remove_from_project('test2', 'testproj')
 | 
			
		||||
    def test_can_remove_user_from_project(self):
 | 
			
		||||
        with user_generator(self.manager, name='test2') as user:
 | 
			
		||||
            with user_and_project_generator(self.manager) as (_user, project):
 | 
			
		||||
                self.manager.add_to_project(user, project)
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertTrue(self.manager.is_project_member(user, project))
 | 
			
		||||
                self.manager.remove_from_project(user, project)
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertFalse(self.manager.is_project_member(user, project))
 | 
			
		||||
 | 
			
		||||
    def test_209_can_generate_x509(self):
 | 
			
		||||
        # MUST HAVE RUN CLOUD SETUP BY NOW
 | 
			
		||||
        self.cloud = cloud.CloudController()
 | 
			
		||||
        self.cloud.setup()
 | 
			
		||||
        _key, cert_str = self.manager._generate_x509_cert('test1', 'testproj')
 | 
			
		||||
        logging.debug(cert_str)
 | 
			
		||||
    def test_can_add_remove_user_with_role(self):
 | 
			
		||||
        with user_generator(self.manager, name='test2') as user:
 | 
			
		||||
            with user_and_project_generator(self.manager) as (_user, project):
 | 
			
		||||
                # NOTE(todd): after modifying users you must reload project
 | 
			
		||||
                self.manager.add_to_project(user, project)
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.manager.add_role(user, 'developer', project)
 | 
			
		||||
                self.assertTrue(self.manager.is_project_member(user, project))
 | 
			
		||||
                self.manager.remove_from_project(user, project)
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertFalse(self.manager.has_role(user, 'developer',
 | 
			
		||||
                                                       project))
 | 
			
		||||
                self.assertFalse(self.manager.is_project_member(user, project))
 | 
			
		||||
 | 
			
		||||
        # Need to verify that it's signed by the right intermediate CA
 | 
			
		||||
        full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
 | 
			
		||||
        int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
 | 
			
		||||
        cloud_cert = crypto.fetch_ca()
 | 
			
		||||
        logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
 | 
			
		||||
        signed_cert = X509.load_cert_string(cert_str)
 | 
			
		||||
        chain_cert = X509.load_cert_string(full_chain)
 | 
			
		||||
        int_cert = X509.load_cert_string(int_cert)
 | 
			
		||||
        cloud_cert = X509.load_cert_string(cloud_cert)
 | 
			
		||||
        self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
 | 
			
		||||
        self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
 | 
			
		||||
    def test_can_generate_x509(self):
 | 
			
		||||
        # NOTE(todd): this doesn't assert against the auth manager
 | 
			
		||||
        #             so it probably belongs in crypto_unittest
 | 
			
		||||
        #             but I'm leaving it where I found it.
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            # NOTE(todd): Should mention why we must setup controller first
 | 
			
		||||
            #             (somebody please clue me in)
 | 
			
		||||
            cloud_controller = cloud.CloudController()
 | 
			
		||||
            cloud_controller.setup()
 | 
			
		||||
            _key, cert_str = self.manager._generate_x509_cert('test1',
 | 
			
		||||
                                                              'testproj')
 | 
			
		||||
            logging.debug(cert_str)
 | 
			
		||||
 | 
			
		||||
        if not FLAGS.use_intermediate_ca:
 | 
			
		||||
            self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
        else:
 | 
			
		||||
            self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
            # Need to verify that it's signed by the right intermediate CA
 | 
			
		||||
            full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
 | 
			
		||||
            int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
 | 
			
		||||
            cloud_cert = crypto.fetch_ca()
 | 
			
		||||
            logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
 | 
			
		||||
            signed_cert = X509.load_cert_string(cert_str)
 | 
			
		||||
            chain_cert = X509.load_cert_string(full_chain)
 | 
			
		||||
            int_cert = X509.load_cert_string(int_cert)
 | 
			
		||||
            cloud_cert = X509.load_cert_string(cloud_cert)
 | 
			
		||||
            self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
 | 
			
		||||
            self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
 | 
			
		||||
            if not FLAGS.use_intermediate_ca:
 | 
			
		||||
                self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
            else:
 | 
			
		||||
                self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
 | 
			
		||||
 | 
			
		||||
    def test_210_can_add_project_role(self):
 | 
			
		||||
        project = self.manager.get_project('testproj')
 | 
			
		||||
        self.assertFalse(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
        self.manager.add_role('test1', 'sysadmin')
 | 
			
		||||
        self.assertFalse(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
        project.add_role('test1', 'sysadmin')
 | 
			
		||||
        self.assertTrue(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
    def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin', project)
 | 
			
		||||
            # NOTE(todd): it will still show up in get_user_roles(u, project)
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
 | 
			
		||||
    def test_211_can_list_project_roles(self):
 | 
			
		||||
        project = self.manager.get_project('testproj')
 | 
			
		||||
        user = self.manager.get_user('test1')
 | 
			
		||||
        self.manager.add_role(user, 'netadmin', project)
 | 
			
		||||
        roles = self.manager.get_user_roles(user)
 | 
			
		||||
        self.assertTrue('sysadmin' in roles)
 | 
			
		||||
        self.assertFalse('netadmin' in roles)
 | 
			
		||||
        project_roles = self.manager.get_user_roles(user, project)
 | 
			
		||||
        self.assertTrue('sysadmin' in project_roles)
 | 
			
		||||
        self.assertTrue('netadmin' in project_roles)
 | 
			
		||||
        # has role should be false because global role is missing
 | 
			
		||||
        self.assertFalse(self.manager.has_role(user, 'netadmin', project))
 | 
			
		||||
    def test_add_user_role_doesnt_infect_project_roles(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
 | 
			
		||||
    def test_can_list_user_roles(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            roles = self.manager.get_user_roles(user)
 | 
			
		||||
            self.assertTrue('sysadmin' in roles)
 | 
			
		||||
            self.assertFalse('netadmin' in roles)
 | 
			
		||||
 | 
			
		||||
    def test_212_can_remove_project_role(self):
 | 
			
		||||
        project = self.manager.get_project('testproj')
 | 
			
		||||
        self.assertTrue(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
        project.remove_role('test1', 'sysadmin')
 | 
			
		||||
        self.assertFalse(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
        self.manager.remove_role('test1', 'sysadmin')
 | 
			
		||||
        self.assertFalse(project.has_role('test1', 'sysadmin'))
 | 
			
		||||
    def test_can_list_project_roles(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin', project)
 | 
			
		||||
            self.manager.add_role(user, 'netadmin', project)
 | 
			
		||||
            project_roles = self.manager.get_user_roles(user, project)
 | 
			
		||||
            self.assertTrue('sysadmin' in project_roles)
 | 
			
		||||
            self.assertTrue('netadmin' in project_roles)
 | 
			
		||||
            # has role should be false user-level role is missing
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'netadmin', project))
 | 
			
		||||
 | 
			
		||||
    def test_214_can_retrieve_project_by_user(self):
 | 
			
		||||
        project = self.manager.create_project('testproj2', 'test2', 'Another test project', ['test2'])
 | 
			
		||||
        self.assert_(len(self.manager.get_projects()) > 1)
 | 
			
		||||
        self.assertEqual(len(self.manager.get_projects('test2')), 1)
 | 
			
		||||
    def test_can_remove_user_roles(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.assertTrue(self.manager.has_role(user, 'sysadmin'))
 | 
			
		||||
            self.manager.remove_role(user, 'sysadmin')
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin'))
 | 
			
		||||
 | 
			
		||||
    def test_220_can_modify_project(self):
 | 
			
		||||
        self.manager.modify_project('testproj', 'test2', 'new description')
 | 
			
		||||
        project = self.manager.get_project('testproj')
 | 
			
		||||
        self.assertEqual(project.project_manager_id, 'test2')
 | 
			
		||||
        self.assertEqual(project.description, 'new description')
 | 
			
		||||
    def test_removing_user_role_hides_it_from_project(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin', project)
 | 
			
		||||
            self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
            self.manager.remove_role(user, 'sysadmin')
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
 | 
			
		||||
    def test_299_can_delete_project(self):
 | 
			
		||||
        self.manager.delete_project('testproj')
 | 
			
		||||
        self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
 | 
			
		||||
        self.manager.delete_project('testproj2')
 | 
			
		||||
    def test_can_remove_project_role_but_keep_user_role(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin')
 | 
			
		||||
            self.manager.add_role(user, 'sysadmin', project)
 | 
			
		||||
            self.assertTrue(self.manager.has_role(user, 'sysadmin'))
 | 
			
		||||
            self.manager.remove_role(user, 'sysadmin', project)
 | 
			
		||||
            self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
 | 
			
		||||
            self.assertTrue(self.manager.has_role(user, 'sysadmin'))
 | 
			
		||||
 | 
			
		||||
    def test_999_can_delete_users(self):
 | 
			
		||||
    def test_can_retrieve_project_by_user(self):
 | 
			
		||||
        with user_and_project_generator(self.manager) as (user, project):
 | 
			
		||||
            self.assertEqual(1, len(self.manager.get_projects('test1')))
 | 
			
		||||
 | 
			
		||||
    def test_can_modify_project(self):
 | 
			
		||||
        with user_and_project_generator(self.manager):
 | 
			
		||||
            with user_generator(self.manager, name='test2'):
 | 
			
		||||
                self.manager.modify_project('testproj', 'test2', 'new desc')
 | 
			
		||||
                project = self.manager.get_project('testproj')
 | 
			
		||||
                self.assertEqual('test2', project.project_manager_id)
 | 
			
		||||
                self.assertEqual('new desc', project.description)
 | 
			
		||||
 | 
			
		||||
    def test_can_delete_project(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            self.manager.create_project('testproj', 'test1')
 | 
			
		||||
            self.assert_(self.manager.get_project('testproj'))
 | 
			
		||||
            self.manager.delete_project('testproj')
 | 
			
		||||
            projectlist = self.manager.get_projects()
 | 
			
		||||
            self.assert_(not filter(lambda p: p.name == 'testproj',
 | 
			
		||||
                         projectlist))
 | 
			
		||||
 | 
			
		||||
    def test_can_delete_user(self):
 | 
			
		||||
        self.manager.create_user('test1')
 | 
			
		||||
        self.assert_(self.manager.get_user('test1'))
 | 
			
		||||
        self.manager.delete_user('test1')
 | 
			
		||||
        users = self.manager.get_users()
 | 
			
		||||
        self.assertFalse(filter(lambda u: u.id == 'test1', users))
 | 
			
		||||
        self.manager.delete_user('test2')
 | 
			
		||||
        self.assertEqual(self.manager.get_user('test2'), None)
 | 
			
		||||
        userlist = self.manager.get_users()
 | 
			
		||||
        self.assert_(not filter(lambda u: u.id == 'test1', userlist))
 | 
			
		||||
 | 
			
		||||
    def test_can_modify_users(self):
 | 
			
		||||
        with user_generator(self.manager):
 | 
			
		||||
            self.manager.modify_user('test1', 'access', 'secret', True)
 | 
			
		||||
            user = self.manager.get_user('test1')
 | 
			
		||||
            self.assertEqual('access', user.access)
 | 
			
		||||
            self.assertEqual('secret', user.secret)
 | 
			
		||||
            self.assertTrue(user.is_admin())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,13 @@
 | 
			
		||||
#    License for the specific language governing permissions and limitations
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import logging
 | 
			
		||||
from M2Crypto import BIO
 | 
			
		||||
from M2Crypto import RSA
 | 
			
		||||
import os
 | 
			
		||||
import StringIO
 | 
			
		||||
import tempfile
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from twisted.internet import defer
 | 
			
		||||
@@ -36,15 +39,22 @@ from nova.auth import manager
 | 
			
		||||
from nova.compute import power_state
 | 
			
		||||
from nova.api.ec2 import context
 | 
			
		||||
from nova.api.ec2 import cloud
 | 
			
		||||
from nova.objectstore import image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FLAGS = flags.FLAGS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Temp dirs for working with image attributes through the cloud controller
 | 
			
		||||
# (stole this from objectstore_unittest.py)
 | 
			
		||||
OSS_TEMPDIR = tempfile.mkdtemp(prefix='test_oss-')
 | 
			
		||||
IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images')
 | 
			
		||||
os.makedirs(IMAGES_PATH)
 | 
			
		||||
 | 
			
		||||
class CloudTestCase(test.TrialTestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(CloudTestCase, self).setUp()
 | 
			
		||||
        self.flags(connection_type='fake')
 | 
			
		||||
        self.flags(connection_type='fake', images_path=IMAGES_PATH)
 | 
			
		||||
 | 
			
		||||
        self.conn = rpc.Connection.instance()
 | 
			
		||||
        logging.getLogger().setLevel(logging.DEBUG)
 | 
			
		||||
@@ -191,3 +201,67 @@ class CloudTestCase(test.TrialTestCase):
 | 
			
		||||
        #for i in xrange(4):
 | 
			
		||||
        #    data = self.cloud.get_metadata(instance(i)['private_dns_name'])
 | 
			
		||||
        #    self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _fake_set_image_description(ctxt, image_id, description):
 | 
			
		||||
        from nova.objectstore import handler
 | 
			
		||||
        class req:
 | 
			
		||||
            pass
 | 
			
		||||
        request = req()
 | 
			
		||||
        request.context = ctxt
 | 
			
		||||
        request.args = {'image_id': [image_id],
 | 
			
		||||
                        'description': [description]}
 | 
			
		||||
 | 
			
		||||
        resource = handler.ImagesResource()
 | 
			
		||||
        resource.render_POST(request)
 | 
			
		||||
 | 
			
		||||
    def test_user_editable_image_endpoint(self):
 | 
			
		||||
        pathdir = os.path.join(FLAGS.images_path, 'ami-testing')
 | 
			
		||||
        os.mkdir(pathdir)
 | 
			
		||||
        info = {'isPublic': False}
 | 
			
		||||
        with open(os.path.join(pathdir, 'info.json'), 'w') as f:
 | 
			
		||||
            json.dump(info, f)
 | 
			
		||||
        img = image.Image('ami-testing')
 | 
			
		||||
        # self.cloud.set_image_description(self.context, 'ami-testing',
 | 
			
		||||
        #                                  'Foo Img')
 | 
			
		||||
        # NOTE(vish): Above won't work unless we start objectstore or create
 | 
			
		||||
        #             a fake version of api/ec2/images.py conn that can
 | 
			
		||||
        #             call methods directly instead of going through boto.
 | 
			
		||||
        #             for now, just cheat and call the method directly
 | 
			
		||||
        self._fake_set_image_description(self.context, 'ami-testing',
 | 
			
		||||
                                         'Foo Img')
 | 
			
		||||
        self.assertEqual('Foo Img', img.metadata['description'])
 | 
			
		||||
        self._fake_set_image_description(self.context, 'ami-testing', '')
 | 
			
		||||
        self.assertEqual('', img.metadata['description'])
 | 
			
		||||
 | 
			
		||||
    def test_update_of_instance_display_fields(self):
 | 
			
		||||
        inst = db.instance_create({}, {})
 | 
			
		||||
        self.cloud.update_instance(self.context, inst['ec2_id'],
 | 
			
		||||
                                   display_name='c00l 1m4g3')
 | 
			
		||||
        inst = db.instance_get({}, inst['id'])
 | 
			
		||||
        self.assertEqual('c00l 1m4g3', inst['display_name'])
 | 
			
		||||
        db.instance_destroy({}, inst['id'])
 | 
			
		||||
 | 
			
		||||
    def test_update_of_instance_wont_update_private_fields(self):
 | 
			
		||||
        inst = db.instance_create({}, {})
 | 
			
		||||
        self.cloud.update_instance(self.context, inst['id'],
 | 
			
		||||
                                   mac_address='DE:AD:BE:EF')
 | 
			
		||||
        inst = db.instance_get({}, inst['id'])
 | 
			
		||||
        self.assertEqual(None, inst['mac_address'])
 | 
			
		||||
        db.instance_destroy({}, inst['id'])
 | 
			
		||||
 | 
			
		||||
    def test_update_of_volume_display_fields(self):
 | 
			
		||||
        vol = db.volume_create({}, {})
 | 
			
		||||
        self.cloud.update_volume(self.context, vol['id'],
 | 
			
		||||
                                 display_name='c00l v0lum3')
 | 
			
		||||
        vol = db.volume_get({}, vol['id'])
 | 
			
		||||
        self.assertEqual('c00l v0lum3', vol['display_name'])
 | 
			
		||||
        db.volume_destroy({}, vol['id'])
 | 
			
		||||
 | 
			
		||||
    def test_update_of_volume_wont_update_private_fields(self):
 | 
			
		||||
        vol = db.volume_create({}, {})
 | 
			
		||||
        self.cloud.update_volume(self.context, vol['id'],
 | 
			
		||||
                                   mountpoint='/not/here')
 | 
			
		||||
        vol = db.volume_get({}, vol['id'])
 | 
			
		||||
        self.assertEqual(None, vol['mountpoint'])
 | 
			
		||||
        db.volume_destroy({}, vol['id'])
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,12 @@ class ObjectStoreTestCase(test.TrialTestCase):
 | 
			
		||||
        self.context.project = self.auth_manager.get_project('proj2')
 | 
			
		||||
        self.assertFalse(my_img.is_authorized(self.context))
 | 
			
		||||
 | 
			
		||||
        # change user-editable fields
 | 
			
		||||
        my_img.update_user_editable_fields({'display_name': 'my cool image'})
 | 
			
		||||
        self.assertEqual('my cool image', my_img.metadata['displayName'])
 | 
			
		||||
        my_img.update_user_editable_fields({'display_name': ''})
 | 
			
		||||
        self.assert_(not my_img.metadata['displayName'])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestHTTPChannel(http.HTTPChannel):
 | 
			
		||||
    """Dummy site required for twisted.web"""
 | 
			
		||||
 
 | 
			
		||||
@@ -103,8 +103,8 @@ class XenAPIConnection(object):
 | 
			
		||||
        self._conn.login_with_password(user, pw)
 | 
			
		||||
 | 
			
		||||
    def list_instances(self):
 | 
			
		||||
        result = [self._conn.xenapi.VM.get_name_label(vm) \
 | 
			
		||||
                  for vm in self._conn.xenapi.VM.get_all()]
 | 
			
		||||
        return [self._conn.xenapi.VM.get_name_label(vm) \
 | 
			
		||||
                for vm in self._conn.xenapi.VM.get_all()]
 | 
			
		||||
 | 
			
		||||
    @defer.inlineCallbacks
 | 
			
		||||
    def spawn(self, instance):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								nova/wsgi.py
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								nova/wsgi.py
									
									
									
									
									
								
							@@ -230,6 +230,15 @@ class Controller(object):
 | 
			
		||||
        serializer = Serializer(request.environ, _metadata)
 | 
			
		||||
        return serializer.to_content_type(data)
 | 
			
		||||
 | 
			
		||||
    def _deserialize(self, data, request):
 | 
			
		||||
        """
 | 
			
		||||
        Deserialize the request body to the response type requested in request.
 | 
			
		||||
        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(request.environ, _metadata)
 | 
			
		||||
        return serializer.deserialize(data)
 | 
			
		||||
 | 
			
		||||
class Serializer(object):
 | 
			
		||||
    """
 | 
			
		||||
@@ -272,10 +281,13 @@ class Serializer(object):
 | 
			
		||||
        The string must be in the format of a supported MIME type.
 | 
			
		||||
        """
 | 
			
		||||
        datastring = datastring.strip()
 | 
			
		||||
        is_xml = (datastring[0] == '<')
 | 
			
		||||
        if not is_xml:
 | 
			
		||||
            return json.loads(datastring)
 | 
			
		||||
        return self._from_xml(datastring)
 | 
			
		||||
        try:
 | 
			
		||||
            is_xml = (datastring[0] == '<')
 | 
			
		||||
            if not is_xml:
 | 
			
		||||
                return json.loads(datastring)
 | 
			
		||||
            return self._from_xml(datastring)
 | 
			
		||||
        except:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def _from_xml(self, datastring):
 | 
			
		||||
        xmldata = self.metadata.get('application/xml', {})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										158
									
								
								tools/setup_iptables.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										158
									
								
								tools/setup_iptables.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,158 @@
 | 
			
		||||
#!/usr/bin/env 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.
 | 
			
		||||
 | 
			
		||||
# NOTE(vish): This script sets up some reasonable defaults for iptables and
 | 
			
		||||
#             creates nova-specific chains.  If you use this script you should
 | 
			
		||||
#             run nova-network and nova-compute with --use_nova_chains=True
 | 
			
		||||
 | 
			
		||||
# NOTE(vish): If you run nova-api on a different port, make sure to change
 | 
			
		||||
#             the port here
 | 
			
		||||
API_PORT=${API_PORT:-"8773"}
 | 
			
		||||
if [ -n "$1" ]; then
 | 
			
		||||
    CMD=$1
 | 
			
		||||
else
 | 
			
		||||
    CMD="all"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ -n "$2" ]; then
 | 
			
		||||
    IP=$2
 | 
			
		||||
else
 | 
			
		||||
    # NOTE(vish): This will just get the first ip in the list, so if you
 | 
			
		||||
    #             have more than one eth device set up, this will fail, and
 | 
			
		||||
    #             you should explicitly pass in the ip of the instance
 | 
			
		||||
    IP=`ifconfig  | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'`
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ -n "$3" ]; then
 | 
			
		||||
    PRIVATE_RANGE=$3
 | 
			
		||||
else
 | 
			
		||||
    PRIVATE_RANGE="10.0.0.0/12"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if [ -n "$4" ]; then
 | 
			
		||||
    # NOTE(vish): Management IP is the ip over which to allow ssh traffic.  It
 | 
			
		||||
    #             will also allow traffic to nova-api
 | 
			
		||||
    MGMT_IP=$4
 | 
			
		||||
else
 | 
			
		||||
    MGMT_IP="$IP"
 | 
			
		||||
fi
 | 
			
		||||
if [ "$CMD" == "clear" ]; then
 | 
			
		||||
    iptables -P INPUT ACCEPT
 | 
			
		||||
    iptables -P FORWARD ACCEPT
 | 
			
		||||
    iptables -P OUTPUT ACCEPT
 | 
			
		||||
    iptables -F
 | 
			
		||||
    iptables -t nat -F
 | 
			
		||||
    iptables -F nova_input
 | 
			
		||||
    iptables -F nova_output
 | 
			
		||||
    iptables -F nova_forward
 | 
			
		||||
    iptables -t nat -F nova_input
 | 
			
		||||
    iptables -t nat -F nova_output
 | 
			
		||||
    iptables -t nat -F nova_forward
 | 
			
		||||
    iptables -t nat -X
 | 
			
		||||
    iptables -X
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -P INPUT DROP
 | 
			
		||||
    iptables -A INPUT -m state --state INVALID -j DROP
 | 
			
		||||
    iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
 | 
			
		||||
    iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT
 | 
			
		||||
    iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT
 | 
			
		||||
    iptables -N nova_input
 | 
			
		||||
    iptables -A INPUT -j nova_input
 | 
			
		||||
    iptables -A INPUT -p icmp -j ACCEPT
 | 
			
		||||
    iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset
 | 
			
		||||
    iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable
 | 
			
		||||
 | 
			
		||||
    iptables -P FORWARD DROP
 | 
			
		||||
    iptables -A FORWARD -m state --state INVALID -j DROP
 | 
			
		||||
    iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
 | 
			
		||||
    iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
 | 
			
		||||
    iptables -N nova_forward
 | 
			
		||||
    iptables -A FORWARD -j nova_forward
 | 
			
		||||
 | 
			
		||||
    # NOTE(vish): DROP on output is too restrictive for now.  We need to add
 | 
			
		||||
    #             in a bunch of more specific output rules to use it.
 | 
			
		||||
    # iptables -P OUTPUT DROP
 | 
			
		||||
    iptables -A OUTPUT -m state --state INVALID -j DROP
 | 
			
		||||
    iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
 | 
			
		||||
    iptables -N nova_output
 | 
			
		||||
    iptables -A OUTPUT -j nova_output
 | 
			
		||||
 | 
			
		||||
    iptables -t nat -N nova_prerouting
 | 
			
		||||
    iptables -t nat -A PREROUTING -j nova_prerouting
 | 
			
		||||
 | 
			
		||||
    iptables -t nat -N nova_postrouting
 | 
			
		||||
    iptables -t nat -A POSTROUTING -j nova_postrouting
 | 
			
		||||
 | 
			
		||||
    iptables -t nat -N nova_output
 | 
			
		||||
    iptables -t nat -A OUTPUT -j nova_output
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "web" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    # NOTE(vish): This opens up ports for web access, allowing web-based
 | 
			
		||||
    #             dashboards to work.
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "objectstore" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport $API_PORT -j ACCEPT
 | 
			
		||||
    if [ "$IP" != "$MGMT_IP" ]; then
 | 
			
		||||
        iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport $API_PORT -j ACCEPT
 | 
			
		||||
    fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "redis" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "mysql" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "rabbitmq" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "dnsmasq" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    # NOTE(vish): this could theoretically be setup per network
 | 
			
		||||
    #             for each host, but it seems like overkill
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT
 | 
			
		||||
    iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "$CMD" == "ldap" ] || [ "$CMD" == "all" ]; then
 | 
			
		||||
    iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user