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