merged trunk
This commit is contained in:
@@ -266,6 +266,18 @@ class UserCommands(object):
|
|||||||
for user in self.manager.get_users():
|
for user in self.manager.get_users():
|
||||||
print user.name
|
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 ProjectCommands(object):
|
||||||
"""Class for managing projects."""
|
"""Class for managing projects."""
|
||||||
@@ -291,7 +303,7 @@ class ProjectCommands(object):
|
|||||||
def environment(self, project_id, user_id, filename='novarc'):
|
def environment(self, project_id, user_id, filename='novarc'):
|
||||||
"""Exports environment variables to an sourcable file
|
"""Exports environment variables to an sourcable file
|
||||||
arguments: project_id user_id [filename='novarc]"""
|
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:
|
with open(filename, 'w') as f:
|
||||||
f.write(rc)
|
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'],
|
'RunInstances': ['projectmanager', 'sysadmin'],
|
||||||
'TerminateInstances': ['projectmanager', 'sysadmin'],
|
'TerminateInstances': ['projectmanager', 'sysadmin'],
|
||||||
'RebootInstances': ['projectmanager', 'sysadmin'],
|
'RebootInstances': ['projectmanager', 'sysadmin'],
|
||||||
|
'UpdateInstance': ['projectmanager', 'sysadmin'],
|
||||||
'DeleteVolume': ['projectmanager', 'sysadmin'],
|
'DeleteVolume': ['projectmanager', 'sysadmin'],
|
||||||
'DescribeImages': ['all'],
|
'DescribeImages': ['all'],
|
||||||
'DeregisterImage': ['projectmanager', 'sysadmin'],
|
'DeregisterImage': ['projectmanager', 'sysadmin'],
|
||||||
'RegisterImage': ['projectmanager', 'sysadmin'],
|
'RegisterImage': ['projectmanager', 'sysadmin'],
|
||||||
'DescribeImageAttribute': ['all'],
|
'DescribeImageAttribute': ['all'],
|
||||||
'ModifyImageAttribute': ['projectmanager', 'sysadmin'],
|
'ModifyImageAttribute': ['projectmanager', 'sysadmin'],
|
||||||
|
'UpdateImage': ['projectmanager', 'sysadmin'],
|
||||||
},
|
},
|
||||||
'AdminController': {
|
'AdminController': {
|
||||||
# All actions have the same permission: ['none'] (the default)
|
# All actions have the same permission: ['none'] (the default)
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from nova import quota
|
|||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.compute.instance_types import INSTANCE_TYPES
|
from nova.compute.instance_types import INSTANCE_TYPES
|
||||||
|
from nova.api import cloud
|
||||||
from nova.api.ec2 import images
|
from nova.api.ec2 import images
|
||||||
|
|
||||||
|
|
||||||
@@ -285,6 +286,9 @@ class CloudController(object):
|
|||||||
'volume_id': volume['ec2_id']}]
|
'volume_id': volume['ec2_id']}]
|
||||||
else:
|
else:
|
||||||
v['attachmentSet'] = [{}]
|
v['attachmentSet'] = [{}]
|
||||||
|
|
||||||
|
v['display_name'] = volume['display_name']
|
||||||
|
v['display_description'] = volume['display_description']
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def create_volume(self, context, size, **kwargs):
|
def create_volume(self, context, size, **kwargs):
|
||||||
@@ -302,6 +306,8 @@ class CloudController(object):
|
|||||||
vol['availability_zone'] = FLAGS.storage_availability_zone
|
vol['availability_zone'] = FLAGS.storage_availability_zone
|
||||||
vol['status'] = "creating"
|
vol['status'] = "creating"
|
||||||
vol['attach_status'] = "detached"
|
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)
|
volume_ref = db.volume_create(context, vol)
|
||||||
|
|
||||||
rpc.cast(FLAGS.scheduler_topic,
|
rpc.cast(FLAGS.scheduler_topic,
|
||||||
@@ -368,6 +374,16 @@ class CloudController(object):
|
|||||||
lst = [lst]
|
lst = [lst]
|
||||||
return [{label: x} for x in 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):
|
def describe_instances(self, context, **kwargs):
|
||||||
return self._format_describe_instances(context)
|
return self._format_describe_instances(context)
|
||||||
|
|
||||||
@@ -420,6 +436,8 @@ class CloudController(object):
|
|||||||
i['instanceType'] = instance['instance_type']
|
i['instanceType'] = instance['instance_type']
|
||||||
i['launchTime'] = instance['created_at']
|
i['launchTime'] = instance['created_at']
|
||||||
i['amiLaunchIndex'] = instance['launch_index']
|
i['amiLaunchIndex'] = instance['launch_index']
|
||||||
|
i['displayName'] = instance['display_name']
|
||||||
|
i['displayDescription'] = instance['display_description']
|
||||||
if not reservations.has_key(instance['reservation_id']):
|
if not reservations.has_key(instance['reservation_id']):
|
||||||
r = {}
|
r = {}
|
||||||
r['reservationId'] = instance['reservation_id']
|
r['reservationId'] = instance['reservation_id']
|
||||||
@@ -577,6 +595,8 @@ class CloudController(object):
|
|||||||
base_options['user_data'] = kwargs.get('user_data', '')
|
base_options['user_data'] = kwargs.get('user_data', '')
|
||||||
base_options['security_group'] = security_group
|
base_options['security_group'] = security_group
|
||||||
base_options['instance_type'] = instance_type
|
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]
|
type_data = INSTANCE_TYPES[instance_type]
|
||||||
base_options['memory_mb'] = type_data['memory_mb']
|
base_options['memory_mb'] = type_data['memory_mb']
|
||||||
@@ -665,12 +685,19 @@ class CloudController(object):
|
|||||||
def reboot_instances(self, context, instance_id, **kwargs):
|
def reboot_instances(self, context, instance_id, **kwargs):
|
||||||
"""instance_id is a list of instance ids"""
|
"""instance_id is a list of instance ids"""
|
||||||
for id_str in instance_id:
|
for id_str in instance_id:
|
||||||
instance_ref = db.instance_get_by_ec2_id(context, id_str)
|
cloud.reboot(id_str, context=context)
|
||||||
host = instance_ref['host']
|
return True
|
||||||
rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host),
|
|
||||||
{"method": "reboot_instance",
|
def update_instance(self, context, instance_id, **kwargs):
|
||||||
"args": {"context": None,
|
updatable_fields = ['display_name', 'display_description']
|
||||||
"instance_id": instance_ref['id']}})
|
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
|
return True
|
||||||
|
|
||||||
def delete_volume(self, context, volume_id, **kwargs):
|
def delete_volume(self, context, volume_id, **kwargs):
|
||||||
@@ -728,3 +755,7 @@ class CloudController(object):
|
|||||||
if not operation_type in ['add', 'remove']:
|
if not operation_type in ['add', 'remove']:
|
||||||
raise exception.ApiError('operation_type must be add or remove')
|
raise exception.ApiError('operation_type must be add or remove')
|
||||||
return images.modify(context, image_id, operation_type)
|
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
|
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):
|
def register(context, image_location):
|
||||||
""" rpc call to register a new image based from a manifest """
|
""" rpc call to register a new image based from a manifest """
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import webob
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import wsgi
|
from nova import wsgi
|
||||||
|
from nova.api.rackspace import faults
|
||||||
from nova.api.rackspace import backup_schedules
|
from nova.api.rackspace import backup_schedules
|
||||||
from nova.api.rackspace import flavors
|
from nova.api.rackspace import flavors
|
||||||
from nova.api.rackspace import images
|
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"])
|
user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"])
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
return webob.exc.HTTPUnauthorized()
|
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||||
|
|
||||||
if not req.environ.has_key('nova.context'):
|
if not req.environ.has_key('nova.context'):
|
||||||
req.environ['nova.context'] = {}
|
req.environ['nova.context'] = {}
|
||||||
@@ -112,8 +113,10 @@ class RateLimitingMiddleware(wsgi.Middleware):
|
|||||||
delay = self.get_delay(action_name, username)
|
delay = self.get_delay(action_name, username)
|
||||||
if delay:
|
if delay:
|
||||||
# TODO(gundlach): Get the retry-after format correct.
|
# TODO(gundlach): Get the retry-after format correct.
|
||||||
raise webob.exc.HTTPRequestEntityTooLarge(headers={
|
exc = webob.exc.HTTPRequestEntityTooLarge(
|
||||||
'Retry-After': time.time() + delay})
|
explanation='Too many requests.',
|
||||||
|
headers={'Retry-After': time.time() + delay})
|
||||||
|
raise faults.Fault(exc)
|
||||||
return self.application
|
return self.application
|
||||||
|
|
||||||
def get_delay(self, action_name, username):
|
def get_delay(self, action_name, username):
|
||||||
@@ -165,3 +168,23 @@ class APIRouter(wsgi.Router):
|
|||||||
controller=sharedipgroups.Controller())
|
controller=sharedipgroups.Controller())
|
||||||
|
|
||||||
super(APIRouter, self).__init__(mapper)
|
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 flags
|
||||||
from nova import manager
|
from nova import manager
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova.api.rackspace import faults
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
@@ -36,13 +37,13 @@ class BasicApiAuthManager(object):
|
|||||||
# honor it
|
# honor it
|
||||||
path_info = req.path_info
|
path_info = req.path_info
|
||||||
if len(path_info) > 1:
|
if len(path_info) > 1:
|
||||||
return webob.exc.HTTPUnauthorized()
|
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
username, key = req.headers['X-Auth-User'], \
|
username, key = req.headers['X-Auth-User'], \
|
||||||
req.headers['X-Auth-Key']
|
req.headers['X-Auth-Key']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return webob.exc.HTTPUnauthorized()
|
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||||
|
|
||||||
username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key']
|
username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key']
|
||||||
token, user = self._authorize_user(username, key)
|
token, user = self._authorize_user(username, key)
|
||||||
@@ -57,7 +58,7 @@ class BasicApiAuthManager(object):
|
|||||||
res.status = '204'
|
res.status = '204'
|
||||||
return res
|
return res
|
||||||
else:
|
else:
|
||||||
return webob.exc.HTTPUnauthorized()
|
return faults.Fault(webob.exc.HTTPUnauthorized())
|
||||||
|
|
||||||
def authorize_token(self, token_hash):
|
def authorize_token(self, token_hash):
|
||||||
""" retrieves user information from the datastore given a token
|
""" retrieves user information from the datastore given a token
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from webob import exc
|
|||||||
|
|
||||||
from nova import wsgi
|
from nova import wsgi
|
||||||
from nova.api.rackspace import _id_translator
|
from nova.api.rackspace import _id_translator
|
||||||
|
from nova.api.rackspace import faults
|
||||||
import nova.image.service
|
import nova.image.service
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
@@ -27,12 +28,12 @@ class Controller(wsgi.Controller):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def index(self, req, server_id):
|
def index(self, req, server_id):
|
||||||
return exc.HTTPNotFound()
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def create(self, req, server_id):
|
def create(self, req, server_id):
|
||||||
""" No actual update method required, since the existing API allows
|
""" No actual update method required, since the existing API allows
|
||||||
both create and update through a POST """
|
both create and update through a POST """
|
||||||
return exc.HTTPNotFound()
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def delete(self, req, server_id):
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from webob import exc
|
||||||
|
|
||||||
|
from nova.api.rackspace import faults
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
from nova import wsgi
|
from nova import wsgi
|
||||||
from webob import exc
|
import nova.api.rackspace
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
"""Flavor controller for the Rackspace API."""
|
"""Flavor controller for the Rackspace API."""
|
||||||
@@ -38,6 +41,7 @@ class Controller(wsgi.Controller):
|
|||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return all flavors in detail."""
|
"""Return all flavors in detail."""
|
||||||
items = [self.show(req, id)['flavor'] for id in self._all_ids()]
|
items = [self.show(req, id)['flavor'] for id in self._all_ids()]
|
||||||
|
items = nova.api.rackspace.limited(items, req)
|
||||||
return dict(flavors=items)
|
return dict(flavors=items)
|
||||||
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
@@ -47,7 +51,7 @@ class Controller(wsgi.Controller):
|
|||||||
item = dict(ram=val['memory_mb'], disk=val['local_gb'],
|
item = dict(ram=val['memory_mb'], disk=val['local_gb'],
|
||||||
id=val['flavorid'], name=name)
|
id=val['flavorid'], name=name)
|
||||||
return dict(flavor=item)
|
return dict(flavor=item)
|
||||||
raise exc.HTTPNotFound()
|
raise faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def _all_ids(self):
|
def _all_ids(self):
|
||||||
"""Return the list of all flavorids."""
|
"""Return the list of all flavorids."""
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ from webob import exc
|
|||||||
|
|
||||||
from nova import wsgi
|
from nova import wsgi
|
||||||
from nova.api.rackspace import _id_translator
|
from nova.api.rackspace import _id_translator
|
||||||
|
import nova.api.rackspace
|
||||||
import nova.image.service
|
import nova.image.service
|
||||||
|
from nova.api.rackspace import faults
|
||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ class Controller(wsgi.Controller):
|
|||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
"""Return all public images in detail."""
|
"""Return all public images in detail."""
|
||||||
data = self._service.index()
|
data = self._service.index()
|
||||||
|
data = nova.api.rackspace.limited(data, req)
|
||||||
for img in data:
|
for img in data:
|
||||||
img['id'] = self._id_translator.to_rs_id(img['id'])
|
img['id'] = self._id_translator.to_rs_id(img['id'])
|
||||||
return dict(images=data)
|
return dict(images=data)
|
||||||
@@ -58,14 +61,14 @@ class Controller(wsgi.Controller):
|
|||||||
|
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
# Only public images are supported for now.
|
# Only public images are supported for now.
|
||||||
raise exc.HTTPNotFound()
|
raise faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def create(self, req):
|
def create(self, req):
|
||||||
# Only public images are supported for now, so a request to
|
# Only public images are supported for now, so a request to
|
||||||
# make a backup of a server cannot be supproted.
|
# make a backup of a server cannot be supproted.
|
||||||
raise exc.HTTPNotFound()
|
raise faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def update(self, req, id):
|
def update(self, req, id):
|
||||||
# Users may not modify public images, and that's all that
|
# Users may not modify public images, and that's all that
|
||||||
# we support for now.
|
# we support for now.
|
||||||
raise exc.HTTPNotFound()
|
raise faults.Fault(exc.HTTPNotFound())
|
||||||
|
|||||||
@@ -17,33 +17,45 @@
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
import webob
|
||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import wsgi
|
from nova import wsgi
|
||||||
|
from nova.api import cloud
|
||||||
from nova.api.rackspace import _id_translator
|
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
|
from nova.compute import power_state
|
||||||
|
import nova.api.rackspace
|
||||||
import nova.image.service
|
import nova.image.service
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
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 """
|
""" Helper method for initializing the image id translator """
|
||||||
service = nova.image.service.ImageService.load()
|
service = nova.image.service.ImageService.load()
|
||||||
return _id_translator.RackspaceAPIIdTranslator(
|
return (service, _id_translator.RackspaceAPIIdTranslator(
|
||||||
"image", service.__class__.__name__)
|
"image", service.__class__.__name__))
|
||||||
|
|
||||||
def _filter_params(inst_dict):
|
def _filter_params(inst_dict):
|
||||||
""" Extracts all updatable parameters for a server update request """
|
""" Extracts all updatable parameters for a server update request """
|
||||||
keys = ['name', 'adminPass']
|
keys = dict(name='name', admin_pass='adminPass')
|
||||||
new_attrs = {}
|
new_attrs = {}
|
||||||
for k in keys:
|
for k, v in keys.items():
|
||||||
if inst_dict.has_key(k):
|
if inst_dict.has_key(v):
|
||||||
new_attrs[k] = inst_dict[k]
|
new_attrs[k] = inst_dict[v]
|
||||||
return new_attrs
|
return new_attrs
|
||||||
|
|
||||||
def _entity_list(entities):
|
def _entity_list(entities):
|
||||||
@@ -82,7 +94,6 @@ def _entity_inst(inst):
|
|||||||
|
|
||||||
class Controller(wsgi.Controller):
|
class Controller(wsgi.Controller):
|
||||||
""" The Server API controller for the Openstack API """
|
""" The Server API controller for the Openstack API """
|
||||||
|
|
||||||
|
|
||||||
_serialization_metadata = {
|
_serialization_metadata = {
|
||||||
'application/xml': {
|
'application/xml': {
|
||||||
@@ -101,42 +112,58 @@ class Controller(wsgi.Controller):
|
|||||||
|
|
||||||
def index(self, req):
|
def index(self, req):
|
||||||
""" Returns a list of server names and ids for a given user """
|
""" Returns a list of server names and ids for a given user """
|
||||||
user_id = req.environ['nova.context']['user']['id']
|
return self._items(req, entity_maker=_entity_inst)
|
||||||
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)
|
|
||||||
|
|
||||||
def detail(self, req):
|
def detail(self, req):
|
||||||
""" Returns a list of server details for a given user """
|
""" 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']
|
user_id = req.environ['nova.context']['user']['id']
|
||||||
res = [_entity_detail(inst)['server'] for inst in
|
instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
|
||||||
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)
|
return _entity_list(res)
|
||||||
|
|
||||||
def show(self, req, id):
|
def show(self, req, id):
|
||||||
""" Returns server details by server 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']
|
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:
|
||||||
if inst.user_id == user_id:
|
if inst.user_id == user_id:
|
||||||
return _entity_detail(inst)
|
return _entity_detail(inst)
|
||||||
raise exc.HTTPNotFound()
|
raise faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def delete(self, req, id):
|
def delete(self, req, id):
|
||||||
""" Destroys a server """
|
""" 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']
|
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:
|
if instance and instance['user_id'] == user_id:
|
||||||
self.db_driver.instance_destroy(None, id)
|
self.db_driver.instance_destroy(None, id)
|
||||||
return exc.HTTPAccepted()
|
return faults.Fault(exc.HTTPAccepted())
|
||||||
return exc.HTTPNotFound()
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
|
|
||||||
def create(self, req):
|
def create(self, req):
|
||||||
""" Creates a new server for a given user """
|
""" 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(
|
rpc.cast(
|
||||||
FLAGS.compute_topic, {
|
FLAGS.compute_topic, {
|
||||||
@@ -146,62 +173,127 @@ class Controller(wsgi.Controller):
|
|||||||
|
|
||||||
def update(self, req, id):
|
def update(self, req, id):
|
||||||
""" Updates the server name or password """
|
""" Updates the server name or password """
|
||||||
if not req.environ.has_key('inst_dict'):
|
inst_id_trans = _instance_id_translator()
|
||||||
return exc.HTTPUnprocessableEntity()
|
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)
|
inst_dict = self._deserialize(req.body, req)
|
||||||
if not instance:
|
|
||||||
return exc.HTTPNotFound()
|
if not inst_dict:
|
||||||
|
return faults.Fault(exc.HTTPUnprocessableEntity())
|
||||||
|
|
||||||
attrs = req.environ['nova.context'].get('model_attributes', None)
|
instance = self.db_driver.instance_get_by_ec2_id(None, inst_id)
|
||||||
if attrs:
|
if not instance or instance.user_id != user_id:
|
||||||
self.db_driver.instance_update(None, id, _filter_params(attrs))
|
return faults.Fault(exc.HTTPNotFound())
|
||||||
return exc.HTTPNoContent()
|
|
||||||
|
self.db_driver.instance_update(None, id,
|
||||||
|
_filter_params(inst_dict['server']))
|
||||||
|
return faults.Fault(exc.HTTPNoContent())
|
||||||
|
|
||||||
def action(self, req, id):
|
def action(self, req, id):
|
||||||
""" multi-purpose method used to reboot, rebuild, and
|
""" multi-purpose method used to reboot, rebuild, and
|
||||||
resize a server """
|
resize a server """
|
||||||
if not req.environ.has_key('inst_dict'):
|
input_dict = self._deserialize(req.body, req)
|
||||||
return exc.HTTPUnprocessableEntity()
|
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."""
|
"""Build instance data structure and save it to the data store."""
|
||||||
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
|
||||||
inst = {}
|
inst = {}
|
||||||
|
|
||||||
env = req.environ['inst_dict']
|
inst_id_trans = _instance_id_translator()
|
||||||
|
|
||||||
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']
|
|
||||||
|
|
||||||
user_id = req.environ['nova.context']['user']['id']
|
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['launch_time'] = ltime
|
||||||
inst['mac_address'] = utils.generate_mac()
|
inst['mac_address'] = utils.generate_mac()
|
||||||
|
inst['project_id'] = user_id
|
||||||
|
|
||||||
inst['project_id'] = env['project']['id']
|
inst['state_description'] = 'scheduling'
|
||||||
inst['reservation_id'] = reservation
|
inst['kernel_id'] = image.get('kernelId', FLAGS.default_kernel)
|
||||||
reservation = utils.generate_uid('r')
|
inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk)
|
||||||
|
inst['reservation_id'] = utils.generate_uid('r')
|
||||||
|
|
||||||
address = self.network.allocate_ip(
|
inst['display_name'] = env['server']['name']
|
||||||
inst['user_id'],
|
inst['display_description'] = env['server']['name']
|
||||||
inst['project_id'],
|
|
||||||
mac=inst['mac_address'])
|
|
||||||
|
|
||||||
inst['private_dns_name'] = str(address)
|
#TODO(dietz) this may be ill advised
|
||||||
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
|
key_pair_ref = self.db_driver.key_pair_get_all_by_user(
|
||||||
inst['user_id'],
|
None, user_id)[0]
|
||||||
inst['project_id'],
|
|
||||||
'default')['bridge_name']
|
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)
|
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
|
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):
|
if not self.__user_exists(uid):
|
||||||
raise exception.NotFound("User %s doesn't exist" % uid)
|
raise exception.NotFound("User %s doesn't exist" % uid)
|
||||||
self.__remove_from_all(uid)
|
self.__remove_from_all(uid)
|
||||||
self.conn.delete_s('uid=%s,%s' % (uid,
|
self.conn.delete_s(self.__uid_to_dn(uid))
|
||||||
FLAGS.ldap_user_subtree))
|
|
||||||
|
|
||||||
def delete_project(self, project_id):
|
def delete_project(self, project_id):
|
||||||
"""Delete a project"""
|
"""Delete a project"""
|
||||||
@@ -265,6 +264,19 @@ class LdapDriver(object):
|
|||||||
self.__delete_roles(project_dn)
|
self.__delete_roles(project_dn)
|
||||||
self.__delete_group(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):
|
def __user_exists(self, uid):
|
||||||
"""Check if user exists"""
|
"""Check if user exists"""
|
||||||
return self.get_user(uid) != None
|
return self.get_user(uid) != None
|
||||||
|
|||||||
@@ -630,6 +630,12 @@ class AuthManager(object):
|
|||||||
with self.driver() as drv:
|
with self.driver() as drv:
|
||||||
drv.delete_user(uid)
|
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):
|
def get_credentials(self, user, project=None):
|
||||||
"""Get credential zip for user in project"""
|
"""Get credential zip for user in project"""
|
||||||
if not isinstance(user, User):
|
if not isinstance(user, User):
|
||||||
|
|||||||
@@ -406,9 +406,12 @@ def network_index_count(context):
|
|||||||
return IMPL.network_index_count(context)
|
return IMPL.network_index_count(context)
|
||||||
|
|
||||||
|
|
||||||
def network_index_create(context, values):
|
def network_index_create_safe(context, values):
|
||||||
"""Create a network index from the values dict"""
|
"""Create a network index from the values dict
|
||||||
return IMPL.network_index_create(context, values)
|
|
||||||
|
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):
|
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 import models
|
||||||
from nova.db.sqlalchemy.session import get_session
|
from nova.db.sqlalchemy.session import get_session
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
|
from sqlalchemy.exc import IntegrityError
|
||||||
from sqlalchemy.orm import joinedload_all
|
from sqlalchemy.orm import joinedload_all
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
from sqlalchemy.sql import exists, func
|
from sqlalchemy.sql import exists, func
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@@ -671,11 +673,14 @@ def network_index_count(_context):
|
|||||||
return models.NetworkIndex.count()
|
return models.NetworkIndex.count()
|
||||||
|
|
||||||
|
|
||||||
def network_index_create(_context, values):
|
def network_index_create_safe(_context, values):
|
||||||
network_index_ref = models.NetworkIndex()
|
network_index_ref = models.NetworkIndex()
|
||||||
for (key, value) in values.iteritems():
|
for (key, value) in values.iteritems():
|
||||||
network_index_ref[key] = value
|
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):
|
def network_set_host(_context, network_id, host_id):
|
||||||
|
|||||||
@@ -199,6 +199,8 @@ class Instance(BASE, NovaBase):
|
|||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
ec2_id = Column(String(10), unique=True)
|
ec2_id = Column(String(10), unique=True)
|
||||||
|
|
||||||
|
admin_pass = Column(String(255))
|
||||||
|
|
||||||
user_id = Column(String(255))
|
user_id = Column(String(255))
|
||||||
project_id = Column(String(255))
|
project_id = Column(String(255))
|
||||||
|
|
||||||
@@ -239,7 +241,6 @@ class Instance(BASE, NovaBase):
|
|||||||
vcpus = Column(Integer)
|
vcpus = Column(Integer)
|
||||||
local_gb = Column(Integer)
|
local_gb = Column(Integer)
|
||||||
|
|
||||||
|
|
||||||
hostname = Column(String(255))
|
hostname = Column(String(255))
|
||||||
host = Column(String(255)) # , ForeignKey('hosts.id'))
|
host = Column(String(255)) # , ForeignKey('hosts.id'))
|
||||||
|
|
||||||
@@ -253,6 +254,10 @@ class Instance(BASE, NovaBase):
|
|||||||
scheduled_at = Column(DateTime)
|
scheduled_at = Column(DateTime)
|
||||||
launched_at = Column(DateTime)
|
launched_at = Column(DateTime)
|
||||||
terminated_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
|
# TODO(vish): see Ewan's email about state improvements, probably
|
||||||
# should be in a driver base class or some such
|
# should be in a driver base class or some such
|
||||||
# vmstate_state = running, halted, suspended, paused
|
# vmstate_state = running, halted, suspended, paused
|
||||||
@@ -289,6 +294,10 @@ class Volume(BASE, NovaBase):
|
|||||||
launched_at = Column(DateTime)
|
launched_at = Column(DateTime)
|
||||||
terminated_at = Column(DateTime)
|
terminated_at = Column(DateTime)
|
||||||
|
|
||||||
|
display_name = Column(String(255))
|
||||||
|
display_description = Column(String(255))
|
||||||
|
|
||||||
|
|
||||||
class Quota(BASE, NovaBase):
|
class Quota(BASE, NovaBase):
|
||||||
"""Represents quota overrides for a project"""
|
"""Represents quota overrides for a project"""
|
||||||
__tablename__ = 'quotas'
|
__tablename__ = 'quotas'
|
||||||
@@ -398,7 +407,7 @@ class NetworkIndex(BASE, NovaBase):
|
|||||||
"""
|
"""
|
||||||
__tablename__ = 'network_indexes'
|
__tablename__ = 'network_indexes'
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
index = Column(Integer)
|
index = Column(Integer, unique=True)
|
||||||
network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
|
network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
|
||||||
network = relationship(Network, backref=backref('network_index',
|
network = relationship(Network, backref=backref('network_index',
|
||||||
uselist=False))
|
uselist=False))
|
||||||
|
|||||||
@@ -188,6 +188,8 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid')
|
|||||||
DEFINE_string('rabbit_password', 'guest', 'rabbit password')
|
DEFINE_string('rabbit_password', 'guest', 'rabbit password')
|
||||||
DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
|
DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
|
||||||
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
|
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',
|
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud',
|
||||||
'Url to ec2 api server')
|
'Url to ec2 api server')
|
||||||
|
|
||||||
|
|||||||
@@ -43,3 +43,10 @@ class Manager(object):
|
|||||||
def periodic_tasks(self, context=None):
|
def periodic_tasks(self, context=None):
|
||||||
"""Tasks to be run at a periodic interval"""
|
"""Tasks to be run at a periodic interval"""
|
||||||
yield
|
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'),
|
flags.DEFINE_string('networks_path', utils.abspath('../networks'),
|
||||||
'Location to keep network config files')
|
'Location to keep network config files')
|
||||||
flags.DEFINE_string('public_interface', 'vlan1',
|
flags.DEFINE_string('public_interface', 'vlan1',
|
||||||
'Interface for public IP addresses')
|
'Interface for public IP addresses')
|
||||||
flags.DEFINE_string('bridge_dev', 'eth0',
|
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)]
|
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):
|
def bind_floating_ip(floating_ip):
|
||||||
"""Bind ip to public interface"""
|
"""Bind ip to public interface"""
|
||||||
@@ -58,37 +79,37 @@ def unbind_floating_ip(floating_ip):
|
|||||||
|
|
||||||
def ensure_vlan_forward(public_ip, port, private_ip):
|
def ensure_vlan_forward(public_ip, port, private_ip):
|
||||||
"""Sets up forwarding rules for vlan"""
|
"""Sets up forwarding rules for vlan"""
|
||||||
_confirm_rule("FORWARD -d %s -p udp --dport 1194 -j ACCEPT" % private_ip)
|
_confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" %
|
||||||
_confirm_rule(
|
private_ip)
|
||||||
"PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194"
|
_confirm_rule("PREROUTING",
|
||||||
|
"-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194"
|
||||||
% (public_ip, port, private_ip))
|
% (public_ip, port, private_ip))
|
||||||
|
|
||||||
|
|
||||||
def ensure_floating_forward(floating_ip, fixed_ip):
|
def ensure_floating_forward(floating_ip, fixed_ip):
|
||||||
"""Ensure floating ip forwarding rule"""
|
"""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))
|
% (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))
|
% (fixed_ip, floating_ip))
|
||||||
# TODO(joshua): Get these from the secgroup datastore entries
|
# 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))
|
% (fixed_ip))
|
||||||
for (protocol, port) in DEFAULT_PORTS:
|
for (protocol, port) in DEFAULT_PORTS:
|
||||||
_confirm_rule(
|
_confirm_rule("FORWARD","-d %s -p %s --dport %s -j ACCEPT"
|
||||||
"FORWARD -d %s -p %s --dport %s -j ACCEPT"
|
|
||||||
% (fixed_ip, protocol, port))
|
% (fixed_ip, protocol, port))
|
||||||
|
|
||||||
|
|
||||||
def remove_floating_forward(floating_ip, fixed_ip):
|
def remove_floating_forward(floating_ip, fixed_ip):
|
||||||
"""Remove forwarding for floating 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))
|
% (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))
|
% (fixed_ip, floating_ip))
|
||||||
_remove_rule("FORWARD -d %s -p icmp -j ACCEPT"
|
_remove_rule("FORWARD", "-d %s -p icmp -j ACCEPT"
|
||||||
% (fixed_ip))
|
% (fixed_ip))
|
||||||
for (protocol, port) in DEFAULT_PORTS:
|
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))
|
% (fixed_ip, protocol, port))
|
||||||
|
|
||||||
|
|
||||||
@@ -124,9 +145,10 @@ def ensure_bridge(bridge, interface, net_attrs=None):
|
|||||||
net_attrs['gateway'],
|
net_attrs['gateway'],
|
||||||
net_attrs['broadcast'],
|
net_attrs['broadcast'],
|
||||||
net_attrs['netmask']))
|
net_attrs['netmask']))
|
||||||
_confirm_rule("FORWARD --in-interface %s -j ACCEPT" % bridge)
|
|
||||||
else:
|
else:
|
||||||
_execute("sudo ifconfig %s up" % bridge)
|
_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):
|
def get_dhcp_hosts(context, network_id):
|
||||||
@@ -195,15 +217,19 @@ def _device_exists(device):
|
|||||||
return not err
|
return not err
|
||||||
|
|
||||||
|
|
||||||
def _confirm_rule(cmd):
|
def _confirm_rule(chain, cmd):
|
||||||
"""Delete and re-add iptables rule"""
|
"""Delete and re-add iptables rule"""
|
||||||
_execute("sudo iptables --delete %s" % (cmd), check_exit_code=False)
|
if FLAGS.use_nova_chains:
|
||||||
_execute("sudo iptables -I %s" % (cmd))
|
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"""
|
"""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):
|
def _dnsmasq_cmd(net):
|
||||||
|
|||||||
@@ -236,6 +236,11 @@ class VlanManager(NetworkManager):
|
|||||||
if num:
|
if num:
|
||||||
logging.debug("Dissassociated %s stale fixed ip(s)", 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):
|
def allocate_fixed_ip(self, context, instance_id, *args, **kwargs):
|
||||||
"""Gets a fixed ip from the pool"""
|
"""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"""
|
This could use a manage command instead of keying off of a flag"""
|
||||||
if not self.db.network_index_count(context):
|
if not self.db.network_index_count(context):
|
||||||
for index in range(FLAGS.num_networks):
|
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):
|
def _on_set_network_host(self, context, network_id):
|
||||||
"""Called when this host becomes the host for a project"""
|
"""Called when this host becomes the host for a project"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
.. automodule:: nova.objectstore
|
.. automodule:: nova.objectstore
|
||||||
:platform: Unix
|
: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:: Jesse Andrews <jesse@ansolabs.com>
|
||||||
.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
|
.. moduleauthor:: Devin Carlen <devin.carlen@gmail.com>
|
||||||
.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
|
.. moduleauthor:: Vishvananda Ishaya <vishvananda@yahoo.com>
|
||||||
|
|||||||
@@ -352,6 +352,8 @@ class ImagesResource(resource.Resource):
|
|||||||
m[u'imageType'] = m['type']
|
m[u'imageType'] = m['type']
|
||||||
elif 'imageType' in m:
|
elif 'imageType' in m:
|
||||||
m[u'type'] = m['imageType']
|
m[u'type'] = m['imageType']
|
||||||
|
if 'displayName' not in m:
|
||||||
|
m[u'displayName'] = u''
|
||||||
return m
|
return m
|
||||||
|
|
||||||
request.write(json.dumps([decorate(i.metadata) for i in images]))
|
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
|
def render_POST(self, request): # pylint: disable-msg=R0201
|
||||||
"""Update image attributes: public/private"""
|
"""Update image attributes: public/private"""
|
||||||
|
|
||||||
|
# image_id required for all requests
|
||||||
image_id = get_argument(request, 'image_id', u'')
|
image_id = get_argument(request, 'image_id', u'')
|
||||||
operation = get_argument(request, 'operation', u'')
|
|
||||||
|
|
||||||
image_object = image.Image(image_id)
|
image_object = image.Image(image_id)
|
||||||
|
|
||||||
if not image_object.is_authorized(request.context):
|
if not image_object.is_authorized(request.context):
|
||||||
|
logging.debug("not authorized for render_POST in images")
|
||||||
raise exception.NotAuthorized
|
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 ''
|
return ''
|
||||||
|
|
||||||
def render_DELETE(self, request): # pylint: disable-msg=R0201
|
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:
|
with open(os.path.join(self.path, 'info.json'), 'w') as f:
|
||||||
json.dump(md, 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
|
@staticmethod
|
||||||
def all():
|
def all():
|
||||||
images = []
|
images = []
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class Service(object, service.Service):
|
|||||||
self.topic = topic
|
self.topic = topic
|
||||||
manager_class = utils.import_class(manager)
|
manager_class = utils.import_class(manager)
|
||||||
self.manager = manager_class(host=host, *args, **kwargs)
|
self.manager = manager_class(host=host, *args, **kwargs)
|
||||||
|
self.manager.init_host()
|
||||||
self.model_disconnected = False
|
self.model_disconnected = False
|
||||||
super(Service, self).__init__(*args, **kwargs)
|
super(Service, self).__init__(*args, **kwargs)
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from nova.api.rackspace import limited
|
||||||
from nova.api.rackspace import RateLimitingMiddleware
|
from nova.api.rackspace import RateLimitingMiddleware
|
||||||
from nova.tests.api.test_helper import *
|
from nova.tests.api.test_helper import *
|
||||||
from webob import Request
|
from webob import Request
|
||||||
@@ -77,3 +78,31 @@ class RateLimitingMiddlewareTest(unittest.TestCase):
|
|||||||
self.assertEqual(middleware.limiter.__class__.__name__, "Limiter")
|
self.assertEqual(middleware.limiter.__class__.__name__, "Limiter")
|
||||||
middleware = RateLimitingMiddleware(APIStub(), service_host='foobar')
|
middleware = RateLimitingMiddleware(APIStub(), service_host='foobar')
|
||||||
self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy")
|
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
|
||||||
import webob.dec
|
import webob.dec
|
||||||
import unittest
|
|
||||||
import stubout
|
|
||||||
import nova.api
|
import nova.api
|
||||||
import nova.api.rackspace.auth
|
import nova.api.rackspace.auth
|
||||||
from nova import auth
|
from nova import auth
|
||||||
from nova.tests.api.rackspace import test_helper
|
from nova.tests.api.rackspace import test_helper
|
||||||
import datetime
|
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class FlavorsTest(unittest.TestCase):
|
|||||||
def test_get_flavor_list(self):
|
def test_get_flavor_list(self):
|
||||||
req = webob.Request.blank('/v1.0/flavors')
|
req = webob.Request.blank('/v1.0/flavors')
|
||||||
res = req.get_response(nova.api.API())
|
res = req.get_response(nova.api.API())
|
||||||
print res
|
|
||||||
|
|
||||||
def test_get_flavor_by_id(self):
|
def test_get_flavor_by_id(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import stubout
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from nova.api.rackspace import images
|
from nova.api.rackspace import images
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import nova.api.rackspace
|
|||||||
from nova.api.rackspace import servers
|
from nova.api.rackspace import servers
|
||||||
import nova.db.api
|
import nova.db.api
|
||||||
from nova.db.sqlalchemy.models import Instance
|
from nova.db.sqlalchemy.models import Instance
|
||||||
|
import nova.rpc
|
||||||
from nova.tests.api.test_helper import *
|
from nova.tests.api.test_helper import *
|
||||||
from nova.tests.api.rackspace import test_helper
|
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_for_testing(self.stubs)
|
||||||
test_helper.stub_out_rate_limiting(self.stubs)
|
test_helper.stub_out_rate_limiting(self.stubs)
|
||||||
test_helper.stub_out_auth(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_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',
|
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
|
||||||
return_servers)
|
return_servers)
|
||||||
|
|
||||||
@@ -67,9 +71,6 @@ class ServersTest(unittest.TestCase):
|
|||||||
self.assertEqual(res_dict['server']['id'], '1')
|
self.assertEqual(res_dict['server']['id'], '1')
|
||||||
self.assertEqual(res_dict['server']['name'], 'server1')
|
self.assertEqual(res_dict['server']['name'], 'server1')
|
||||||
|
|
||||||
def test_get_backup_schedule(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_get_server_list(self):
|
def test_get_server_list(self):
|
||||||
req = webob.Request.blank('/v1.0/servers')
|
req = webob.Request.blank('/v1.0/servers')
|
||||||
res = req.get_response(nova.api.API())
|
res = req.get_response(nova.api.API())
|
||||||
@@ -82,24 +83,86 @@ class ServersTest(unittest.TestCase):
|
|||||||
self.assertEqual(s.get('imageId', None), None)
|
self.assertEqual(s.get('imageId', None), None)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
#def test_create_instance(self):
|
def test_create_instance(self):
|
||||||
# test_helper.stub_out_image_translator(self.stubs)
|
def server_update(context, id, params):
|
||||||
# body = dict(server=dict(
|
pass
|
||||||
# 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())
|
def instance_create(context, inst):
|
||||||
|
class Foo(object):
|
||||||
|
ec2_id = 1
|
||||||
|
return Foo()
|
||||||
|
|
||||||
# print res
|
def fake_method(*args, **kwargs):
|
||||||
def test_update_server_password(self):
|
pass
|
||||||
pass
|
|
||||||
|
def project_get_network(context, user_id):
|
||||||
|
return dict(id='1', host='localhost')
|
||||||
|
|
||||||
def test_update_server_name(self):
|
def queue_get_for(context, *args):
|
||||||
pass
|
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):
|
def test_create_backup_schedules(self):
|
||||||
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
|
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import stubout
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from nova.api.rackspace import sharedipgroups
|
from nova.api.rackspace import sharedipgroups
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from nova import utils
|
|||||||
from nova import flags
|
from nova import flags
|
||||||
import nova.api.rackspace.auth
|
import nova.api.rackspace.auth
|
||||||
import nova.api.rackspace._id_translator
|
import nova.api.rackspace._id_translator
|
||||||
|
from nova.image import service
|
||||||
from nova.wsgi import Router
|
from nova.wsgi import Router
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
@@ -40,7 +41,19 @@ def fake_wsgi(self, req):
|
|||||||
req.environ['inst_dict'] = json.loads(req.body)
|
req.environ['inst_dict'] = json.loads(req.body)
|
||||||
return self.application
|
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):
|
class FakeTranslator(object):
|
||||||
def __init__(self, id_type, service_name):
|
def __init__(self, id_type, service_name):
|
||||||
pass
|
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
|
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):
|
def __enter__(self):
|
||||||
flush_db = False
|
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):
|
def setUp(self):
|
||||||
super(AuthTestCase, self).setUp()
|
super(AuthManagerTestCase, self).setUp()
|
||||||
self.flags(connection_type='fake')
|
self.flags(connection_type='fake')
|
||||||
self.manager = manager.AuthManager()
|
self.manager = manager.AuthManager()
|
||||||
|
|
||||||
def test_001_can_create_users(self):
|
def test_create_and_find_user(self):
|
||||||
self.manager.create_user('test1', 'access', 'secret')
|
with user_generator(self.manager):
|
||||||
self.manager.create_user('test2')
|
self.assert_(self.manager.get_user('test1'))
|
||||||
|
|
||||||
def test_002_can_get_user(self):
|
def test_create_and_find_with_properties(self):
|
||||||
user = self.manager.get_user('test1')
|
with user_generator(self.manager, name="herbert", secret="classified",
|
||||||
|
access="private-party"):
|
||||||
def test_003_can_retreive_properties(self):
|
u = self.manager.get_user('herbert')
|
||||||
user = self.manager.get_user('test1')
|
self.assertEqual('herbert', u.id)
|
||||||
self.assertEqual('test1', user.id)
|
self.assertEqual('herbert', u.name)
|
||||||
self.assertEqual('access', user.access)
|
self.assertEqual('classified', u.secret)
|
||||||
self.assertEqual('secret', user.secret)
|
self.assertEqual('private-party', u.access)
|
||||||
|
|
||||||
def test_004_signature_is_valid(self):
|
def test_004_signature_is_valid(self):
|
||||||
#self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? ))
|
#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 S3_URL="http://127.0.0.1:3333/"\n' +
|
||||||
'export EC2_USER_ID="test1"\n')
|
'export EC2_USER_ID="test1"\n')
|
||||||
|
|
||||||
def test_010_can_list_users(self):
|
def test_can_list_users(self):
|
||||||
users = self.manager.get_users()
|
with user_generator(self.manager):
|
||||||
logging.warn(users)
|
with user_generator(self.manager, name="test2"):
|
||||||
self.assertTrue(filter(lambda u: u.id == 'test1', users))
|
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_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_101_can_add_user_role(self):
|
def test_can_create_and_get_project(self):
|
||||||
self.assertFalse(self.manager.has_role('test1', 'itsec'))
|
with user_and_project_generator(self.manager) as (u,p):
|
||||||
self.manager.add_role('test1', 'itsec')
|
self.assert_(self.manager.get_user('test1'))
|
||||||
self.assertTrue(self.manager.has_role('test1', 'itsec'))
|
self.assert_(self.manager.get_user('test1'))
|
||||||
|
self.assert_(self.manager.get_project('testproj'))
|
||||||
|
|
||||||
def test_199_can_remove_user_role(self):
|
def test_can_list_projects(self):
|
||||||
self.assertTrue(self.manager.has_role('test1', 'itsec'))
|
with user_and_project_generator(self.manager):
|
||||||
self.manager.remove_role('test1', 'itsec')
|
with project_generator(self.manager, name="testproj2"):
|
||||||
self.assertFalse(self.manager.has_role('test1', 'itsec'))
|
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_201_can_create_project(self):
|
def test_can_create_and_get_project_with_attributes(self):
|
||||||
project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1'])
|
with user_generator(self.manager):
|
||||||
self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
|
with project_generator(self.manager, description='A test project'):
|
||||||
self.assertEqual(project.name, 'testproj')
|
project = self.manager.get_project('testproj')
|
||||||
self.assertEqual(project.description, 'A test project')
|
self.assertEqual('A test project', project.description)
|
||||||
self.assertEqual(project.project_manager_id, 'test1')
|
|
||||||
self.assertTrue(project.has_member('test1'))
|
|
||||||
|
|
||||||
def test_202_user1_is_project_member(self):
|
def test_can_create_project_with_manager(self):
|
||||||
self.assertTrue(self.manager.get_user('test1').is_project_member('testproj'))
|
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_203_user2_is_not_project_member(self):
|
def test_create_project_assigns_manager_to_members(self):
|
||||||
self.assertFalse(self.manager.get_user('test2').is_project_member('testproj'))
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
|
self.assertTrue(self.manager.is_project_member(user, project))
|
||||||
|
|
||||||
def test_204_user1_is_project_manager(self):
|
def test_no_extra_project_members(self):
|
||||||
self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj'))
|
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_205_user2_is_not_project_manager(self):
|
def test_no_extra_project_managers(self):
|
||||||
self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj'))
|
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_206_can_add_user_to_project(self):
|
def test_can_add_user_to_project(self):
|
||||||
self.manager.add_to_project('test2', 'testproj')
|
with user_generator(self.manager, name='test2') as user:
|
||||||
self.assertTrue(self.manager.get_project('testproj').has_member('test2'))
|
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_207_can_remove_user_from_project(self):
|
def test_can_remove_user_from_project(self):
|
||||||
self.manager.remove_from_project('test2', 'testproj')
|
with user_generator(self.manager, name='test2') as user:
|
||||||
self.assertFalse(self.manager.get_project('testproj').has_member('test2'))
|
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_208_can_remove_add_user_with_role(self):
|
def test_can_add_remove_user_with_role(self):
|
||||||
self.manager.add_to_project('test2', 'testproj')
|
with user_generator(self.manager, name='test2') as user:
|
||||||
self.manager.add_role('test2', 'developer', 'testproj')
|
with user_and_project_generator(self.manager) as (_user, project):
|
||||||
self.manager.remove_from_project('test2', 'testproj')
|
# NOTE(todd): after modifying users you must reload project
|
||||||
self.assertFalse(self.manager.has_role('test2', 'developer', 'testproj'))
|
self.manager.add_to_project(user, project)
|
||||||
self.manager.add_to_project('test2', 'testproj')
|
project = self.manager.get_project('testproj')
|
||||||
self.manager.remove_from_project('test2', '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))
|
||||||
|
|
||||||
def test_209_can_generate_x509(self):
|
def test_can_generate_x509(self):
|
||||||
# MUST HAVE RUN CLOUD SETUP BY NOW
|
# NOTE(todd): this doesn't assert against the auth manager
|
||||||
self.cloud = cloud.CloudController()
|
# so it probably belongs in crypto_unittest
|
||||||
self.cloud.setup()
|
# but I'm leaving it where I found it.
|
||||||
_key, cert_str = self.manager._generate_x509_cert('test1', 'testproj')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
logging.debug(cert_str)
|
# 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)
|
||||||
|
|
||||||
# Need to verify that it's signed by the right intermediate CA
|
# Need to verify that it's signed by the right intermediate CA
|
||||||
full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
|
full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
|
||||||
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
|
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
|
||||||
cloud_cert = crypto.fetch_ca()
|
cloud_cert = crypto.fetch_ca()
|
||||||
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
|
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
|
||||||
signed_cert = X509.load_cert_string(cert_str)
|
signed_cert = X509.load_cert_string(cert_str)
|
||||||
chain_cert = X509.load_cert_string(full_chain)
|
chain_cert = X509.load_cert_string(full_chain)
|
||||||
int_cert = X509.load_cert_string(int_cert)
|
int_cert = X509.load_cert_string(int_cert)
|
||||||
cloud_cert = X509.load_cert_string(cloud_cert)
|
cloud_cert = X509.load_cert_string(cloud_cert)
|
||||||
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
|
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
|
||||||
self.assertTrue(signed_cert.verify(int_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()))
|
||||||
|
|
||||||
if not FLAGS.use_intermediate_ca:
|
def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
|
||||||
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
else:
|
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||||
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
|
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_210_can_add_project_role(self):
|
def test_add_user_role_doesnt_infect_project_roles(self):
|
||||||
project = self.manager.get_project('testproj')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
self.assertFalse(project.has_role('test1', 'sysadmin'))
|
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||||
self.manager.add_role('test1', 'sysadmin')
|
self.manager.add_role(user, 'sysadmin')
|
||||||
self.assertFalse(project.has_role('test1', 'sysadmin'))
|
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||||
project.add_role('test1', 'sysadmin')
|
|
||||||
self.assertTrue(project.has_role('test1', 'sysadmin'))
|
|
||||||
|
|
||||||
def test_211_can_list_project_roles(self):
|
def test_can_list_user_roles(self):
|
||||||
project = self.manager.get_project('testproj')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
user = self.manager.get_user('test1')
|
self.manager.add_role(user, 'sysadmin')
|
||||||
self.manager.add_role(user, 'netadmin', project)
|
roles = self.manager.get_user_roles(user)
|
||||||
roles = self.manager.get_user_roles(user)
|
self.assertTrue('sysadmin' in roles)
|
||||||
self.assertTrue('sysadmin' in roles)
|
self.assertFalse('netadmin' 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_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_212_can_remove_project_role(self):
|
def test_can_remove_user_roles(self):
|
||||||
project = self.manager.get_project('testproj')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
self.assertTrue(project.has_role('test1', 'sysadmin'))
|
self.manager.add_role(user, 'sysadmin')
|
||||||
project.remove_role('test1', 'sysadmin')
|
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
|
||||||
self.assertFalse(project.has_role('test1', 'sysadmin'))
|
self.manager.remove_role(user, 'sysadmin')
|
||||||
self.manager.remove_role('test1', 'sysadmin')
|
self.assertFalse(self.manager.has_role(user, 'sysadmin'))
|
||||||
self.assertFalse(project.has_role('test1', 'sysadmin'))
|
|
||||||
|
|
||||||
def test_214_can_retrieve_project_by_user(self):
|
def test_removing_user_role_hides_it_from_project(self):
|
||||||
project = self.manager.create_project('testproj2', 'test2', 'Another test project', ['test2'])
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
self.assert_(len(self.manager.get_projects()) > 1)
|
self.manager.add_role(user, 'sysadmin')
|
||||||
self.assertEqual(len(self.manager.get_projects('test2')), 1)
|
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_220_can_modify_project(self):
|
def test_can_remove_project_role_but_keep_user_role(self):
|
||||||
self.manager.modify_project('testproj', 'test2', 'new description')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
project = self.manager.get_project('testproj')
|
self.manager.add_role(user, 'sysadmin')
|
||||||
self.assertEqual(project.project_manager_id, 'test2')
|
self.manager.add_role(user, 'sysadmin', project)
|
||||||
self.assertEqual(project.description, 'new description')
|
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_299_can_delete_project(self):
|
def test_can_retrieve_project_by_user(self):
|
||||||
self.manager.delete_project('testproj')
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
|
self.assertEqual(1, len(self.manager.get_projects('test1')))
|
||||||
self.manager.delete_project('testproj2')
|
|
||||||
|
|
||||||
def test_999_can_delete_users(self):
|
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')
|
self.manager.delete_user('test1')
|
||||||
users = self.manager.get_users()
|
userlist = self.manager.get_users()
|
||||||
self.assertFalse(filter(lambda u: u.id == 'test1', users))
|
self.assert_(not filter(lambda u: u.id == 'test1', userlist))
|
||||||
self.manager.delete_user('test2')
|
|
||||||
self.assertEqual(self.manager.get_user('test2'), None)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -16,10 +16,13 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
from M2Crypto import BIO
|
from M2Crypto import BIO
|
||||||
from M2Crypto import RSA
|
from M2Crypto import RSA
|
||||||
|
import os
|
||||||
import StringIO
|
import StringIO
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
@@ -36,15 +39,22 @@ from nova.auth import manager
|
|||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
from nova.api.ec2 import context
|
from nova.api.ec2 import context
|
||||||
from nova.api.ec2 import cloud
|
from nova.api.ec2 import cloud
|
||||||
|
from nova.objectstore import image
|
||||||
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
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):
|
class CloudTestCase(test.TrialTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CloudTestCase, self).setUp()
|
super(CloudTestCase, self).setUp()
|
||||||
self.flags(connection_type='fake')
|
self.flags(connection_type='fake', images_path=IMAGES_PATH)
|
||||||
|
|
||||||
self.conn = rpc.Connection.instance()
|
self.conn = rpc.Connection.instance()
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
@@ -191,3 +201,67 @@ class CloudTestCase(test.TrialTestCase):
|
|||||||
#for i in xrange(4):
|
#for i in xrange(4):
|
||||||
# data = self.cloud.get_metadata(instance(i)['private_dns_name'])
|
# data = self.cloud.get_metadata(instance(i)['private_dns_name'])
|
||||||
# self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i)
|
# 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.context.project = self.auth_manager.get_project('proj2')
|
||||||
self.assertFalse(my_img.is_authorized(self.context))
|
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):
|
class TestHTTPChannel(http.HTTPChannel):
|
||||||
"""Dummy site required for twisted.web"""
|
"""Dummy site required for twisted.web"""
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ class XenAPIConnection(object):
|
|||||||
self._conn.login_with_password(user, pw)
|
self._conn.login_with_password(user, pw)
|
||||||
|
|
||||||
def list_instances(self):
|
def list_instances(self):
|
||||||
result = [self._conn.xenapi.VM.get_name_label(vm) \
|
return [self._conn.xenapi.VM.get_name_label(vm) \
|
||||||
for vm in self._conn.xenapi.VM.get_all()]
|
for vm in self._conn.xenapi.VM.get_all()]
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def spawn(self, instance):
|
def spawn(self, instance):
|
||||||
|
|||||||
20
nova/wsgi.py
20
nova/wsgi.py
@@ -230,6 +230,15 @@ class Controller(object):
|
|||||||
serializer = Serializer(request.environ, _metadata)
|
serializer = Serializer(request.environ, _metadata)
|
||||||
return serializer.to_content_type(data)
|
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):
|
class Serializer(object):
|
||||||
"""
|
"""
|
||||||
@@ -272,10 +281,13 @@ class Serializer(object):
|
|||||||
The string must be in the format of a supported MIME type.
|
The string must be in the format of a supported MIME type.
|
||||||
"""
|
"""
|
||||||
datastring = datastring.strip()
|
datastring = datastring.strip()
|
||||||
is_xml = (datastring[0] == '<')
|
try:
|
||||||
if not is_xml:
|
is_xml = (datastring[0] == '<')
|
||||||
return json.loads(datastring)
|
if not is_xml:
|
||||||
return self._from_xml(datastring)
|
return json.loads(datastring)
|
||||||
|
return self._from_xml(datastring)
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
def _from_xml(self, datastring):
|
def _from_xml(self, datastring):
|
||||||
xmldata = self.metadata.get('application/xml', {})
|
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