merged trunk

This commit is contained in:
Vishvananda Ishaya
2010-09-30 18:13:45 -07:00
40 changed files with 1202 additions and 265 deletions

View File

@@ -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
View 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']}})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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)

View 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

View File

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

View File

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

View File

@@ -17,33 +17,45 @@
import time
import webob
from webob import exc
from nova import flags
from nova import rpc
from nova import utils
from nova import wsgi
from nova.api import cloud
from nova.api.rackspace import _id_translator
from nova.api.rackspace import context
from nova.api.rackspace import faults
from nova.compute import instance_types
from nova.compute import power_state
import nova.api.rackspace
import nova.image.service
FLAGS = flags.FLAGS
flags.DEFINE_string('rs_network_manager', 'nova.network.manager.FlatManager',
'Networking for rackspace')
def _instance_id_translator():
""" Helper method for initializing an id translator for Rackspace instance
ids """
return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova')
def translator_instance():
def _image_service():
""" Helper method for initializing the image id translator """
service = nova.image.service.ImageService.load()
return _id_translator.RackspaceAPIIdTranslator(
"image", service.__class__.__name__)
return (service, _id_translator.RackspaceAPIIdTranslator(
"image", service.__class__.__name__))
def _filter_params(inst_dict):
""" Extracts all updatable parameters for a server update request """
keys = ['name', 'adminPass']
keys = dict(name='name', admin_pass='adminPass')
new_attrs = {}
for k in keys:
if inst_dict.has_key(k):
new_attrs[k] = inst_dict[k]
for k, v in keys.items():
if inst_dict.has_key(v):
new_attrs[k] = inst_dict[v]
return new_attrs
def _entity_list(entities):
@@ -83,7 +95,6 @@ def _entity_inst(inst):
class Controller(wsgi.Controller):
""" The Server API controller for the Openstack API """
_serialization_metadata = {
'application/xml': {
"attributes": {
@@ -101,42 +112,58 @@ class Controller(wsgi.Controller):
def index(self, req):
""" Returns a list of server names and ids for a given user """
user_id = req.environ['nova.context']['user']['id']
instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
res = [_entity_inst(inst)['server'] for inst in instance_list]
return _entity_list(res)
return self._items(req, entity_maker=_entity_inst)
def detail(self, req):
""" Returns a list of server details for a given user """
return self._items(req, entity_maker=_entity_detail)
def _items(self, req, entity_maker):
"""Returns a list of servers for a given user.
entity_maker - either _entity_detail or _entity_inst
"""
user_id = req.environ['nova.context']['user']['id']
res = [_entity_detail(inst)['server'] for inst in
self.db_driver.instance_get_all_by_user(None, user_id)]
instance_list = self.db_driver.instance_get_all_by_user(None, user_id)
limited_list = nova.api.rackspace.limited(instance_list, req)
res = [entity_maker(inst)['server'] for inst in limited_list]
return _entity_list(res)
def show(self, req, id):
""" Returns server details by server id """
inst_id_trans = _instance_id_translator()
inst_id = inst_id_trans.from_rs_id(id)
user_id = req.environ['nova.context']['user']['id']
inst = self.db_driver.instance_get(None, id)
inst = self.db_driver.instance_get_by_ec2_id(None, inst_id)
if inst:
if inst.user_id == user_id:
return _entity_detail(inst)
raise exc.HTTPNotFound()
raise faults.Fault(exc.HTTPNotFound())
def delete(self, req, id):
""" Destroys a server """
inst_id_trans = _instance_id_translator()
inst_id = inst_id_trans.from_rs_id(id)
user_id = req.environ['nova.context']['user']['id']
instance = self.db_driver.instance_get(None, id)
instance = self.db_driver.instance_get_by_ec2_id(None, inst_id)
if instance and instance['user_id'] == user_id:
self.db_driver.instance_destroy(None, id)
return exc.HTTPAccepted()
return exc.HTTPNotFound()
return faults.Fault(exc.HTTPAccepted())
return faults.Fault(exc.HTTPNotFound())
def create(self, req):
""" Creates a new server for a given user """
if not req.environ.has_key('inst_dict'):
return exc.HTTPUnprocessableEntity()
inst = self._build_server_instance(req)
env = self._deserialize(req.body, req)
if not env:
return faults.Fault(exc.HTTPUnprocessableEntity())
try:
inst = self._build_server_instance(req, env)
except Exception, e:
return faults.Fault(exc.HTTPUnprocessableEntity())
rpc.cast(
FLAGS.compute_topic, {
@@ -146,62 +173,127 @@ class Controller(wsgi.Controller):
def update(self, req, id):
""" Updates the server name or password """
if not req.environ.has_key('inst_dict'):
return exc.HTTPUnprocessableEntity()
inst_id_trans = _instance_id_translator()
inst_id = inst_id_trans.from_rs_id(id)
user_id = req.environ['nova.context']['user']['id']
instance = self.db_driver.instance_get(None, id)
if not instance:
return exc.HTTPNotFound()
inst_dict = self._deserialize(req.body, req)
attrs = req.environ['nova.context'].get('model_attributes', None)
if attrs:
self.db_driver.instance_update(None, id, _filter_params(attrs))
return exc.HTTPNoContent()
if not inst_dict:
return faults.Fault(exc.HTTPUnprocessableEntity())
instance = self.db_driver.instance_get_by_ec2_id(None, inst_id)
if not instance or instance.user_id != user_id:
return faults.Fault(exc.HTTPNotFound())
self.db_driver.instance_update(None, id,
_filter_params(inst_dict['server']))
return faults.Fault(exc.HTTPNoContent())
def action(self, req, id):
""" multi-purpose method used to reboot, rebuild, and
resize a server """
if not req.environ.has_key('inst_dict'):
return exc.HTTPUnprocessableEntity()
input_dict = self._deserialize(req.body, req)
try:
reboot_type = input_dict['reboot']['type']
except Exception:
raise faults.Fault(webob.exc.HTTPNotImplemented())
opaque_id = _instance_id_translator().from_rs_id(id)
cloud.reboot(opaque_id)
def _build_server_instance(self, req):
def _build_server_instance(self, req, env):
"""Build instance data structure and save it to the data store."""
ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
inst = {}
env = req.environ['inst_dict']
image_id = env['server']['imageId']
opaque_id = translator_instance().from_rs_id(image_id)
inst['name'] = env['server']['server_name']
inst['image_id'] = opaque_id
inst['instance_type'] = env['server']['flavorId']
inst_id_trans = _instance_id_translator()
user_id = req.environ['nova.context']['user']['id']
inst['user_id'] = user_id
flavor_id = env['server']['flavorId']
instance_type, flavor = [(k, v) for k, v in
instance_types.INSTANCE_TYPES.iteritems()
if v['flavorid'] == flavor_id][0]
image_id = env['server']['imageId']
img_service, image_id_trans = _image_service()
opaque_image_id = image_id_trans.to_rs_id(image_id)
image = img_service.show(opaque_image_id)
if not image:
raise Exception, "Image not found"
inst['server_name'] = env['server']['name']
inst['image_id'] = opaque_image_id
inst['user_id'] = user_id
inst['launch_time'] = ltime
inst['mac_address'] = utils.generate_mac()
inst['project_id'] = user_id
inst['project_id'] = env['project']['id']
inst['reservation_id'] = reservation
reservation = utils.generate_uid('r')
inst['state_description'] = 'scheduling'
inst['kernel_id'] = image.get('kernelId', FLAGS.default_kernel)
inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk)
inst['reservation_id'] = utils.generate_uid('r')
address = self.network.allocate_ip(
inst['user_id'],
inst['project_id'],
mac=inst['mac_address'])
inst['display_name'] = env['server']['name']
inst['display_description'] = env['server']['name']
inst['private_dns_name'] = str(address)
inst['bridge_name'] = network.BridgedNetwork.get_network_for_project(
inst['user_id'],
inst['project_id'],
'default')['bridge_name']
#TODO(dietz) this may be ill advised
key_pair_ref = self.db_driver.key_pair_get_all_by_user(
None, user_id)[0]
inst['key_data'] = key_pair_ref['public_key']
inst['key_name'] = key_pair_ref['name']
#TODO(dietz) stolen from ec2 api, see TODO there
inst['security_group'] = 'default'
# Flavor related attributes
inst['instance_type'] = instance_type
inst['memory_mb'] = flavor['memory_mb']
inst['vcpus'] = flavor['vcpus']
inst['local_gb'] = flavor['local_gb']
ref = self.db_driver.instance_create(None, inst)
inst['id'] = ref.id
inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id)
# TODO(dietz): this isn't explicitly necessary, but the networking
# calls depend on an object with a project_id property, and therefore
# should be cleaned up later
api_context = context.APIRequestContext(user_id)
inst['mac_address'] = utils.generate_mac()
#TODO(dietz) is this necessary?
inst['launch_index'] = 0
inst['hostname'] = ref.ec2_id
self.db_driver.instance_update(None, inst['id'], inst)
network_manager = utils.import_object(FLAGS.rs_network_manager)
address = network_manager.allocate_fixed_ip(api_context,
inst['id'])
# TODO(vish): This probably should be done in the scheduler
# network is setup when host is assigned
network_topic = self._get_network_topic(user_id)
rpc.call(network_topic,
{"method": "setup_fixed_ip",
"args": {"context": None,
"address": address}})
return inst
def _get_network_topic(self, user_id):
"""Retrieves the network host for a project"""
network_ref = self.db_driver.project_get_network(None,
user_id)
host = network_ref['host']
if not host:
host = rpc.call(FLAGS.network_topic,
{"method": "set_network_host",
"args": {"context": None,
"project_id": user_id}})
return self.db_driver.queue_get_for(None, FLAGS.network_topic, host)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 = []

View File

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

View File

@@ -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), [])

View File

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

View File

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

View File

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

View File

@@ -26,6 +26,7 @@ import nova.api.rackspace
from nova.api.rackspace import servers
import nova.db.api
from nova.db.sqlalchemy.models import Instance
import nova.rpc
from nova.tests.api.test_helper import *
from nova.tests.api.rackspace import test_helper
@@ -52,8 +53,11 @@ class ServersTest(unittest.TestCase):
test_helper.stub_for_testing(self.stubs)
test_helper.stub_out_rate_limiting(self.stubs)
test_helper.stub_out_auth(self.stubs)
test_helper.stub_out_id_translator(self.stubs)
test_helper.stub_out_key_pair_funcs(self.stubs)
test_helper.stub_out_image_service(self.stubs)
self.stubs.Set(nova.db.api, 'instance_get_all', return_servers)
self.stubs.Set(nova.db.api, 'instance_get', return_server)
self.stubs.Set(nova.db.api, 'instance_get_by_ec2_id', return_server)
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers)
@@ -67,9 +71,6 @@ class ServersTest(unittest.TestCase):
self.assertEqual(res_dict['server']['id'], '1')
self.assertEqual(res_dict['server']['name'], 'server1')
def test_get_backup_schedule(self):
pass
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
res = req.get_response(nova.api.API())
@@ -82,24 +83,86 @@ class ServersTest(unittest.TestCase):
self.assertEqual(s.get('imageId', None), None)
i += 1
#def test_create_instance(self):
# test_helper.stub_out_image_translator(self.stubs)
# body = dict(server=dict(
# name='server_test', imageId=2, flavorId=2, metadata={},
# personality = {}
# ))
# req = webob.Request.blank('/v1.0/servers')
# req.method = 'POST'
# req.body = json.dumps(body)
def test_create_instance(self):
def server_update(context, id, params):
pass
# res = req.get_response(nova.api.API())
def instance_create(context, inst):
class Foo(object):
ec2_id = 1
return Foo()
# print res
def test_update_server_password(self):
pass
def fake_method(*args, **kwargs):
pass
def test_update_server_name(self):
pass
def project_get_network(context, user_id):
return dict(id='1', host='localhost')
def queue_get_for(context, *args):
return 'network_topic'
self.stubs.Set(nova.db.api, 'project_get_network', project_get_network)
self.stubs.Set(nova.db.api, 'instance_create', instance_create)
self.stubs.Set(nova.rpc, 'cast', fake_method)
self.stubs.Set(nova.rpc, 'call', fake_method)
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
self.stubs.Set(nova.db.api, 'queue_get_for', queue_get_for)
self.stubs.Set(nova.network.manager.FlatManager, 'allocate_fixed_ip',
fake_method)
test_helper.stub_out_id_translator(self.stubs)
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality = {}
))
req = webob.Request.blank('/v1.0/servers')
req.method = 'POST'
req.body = json.dumps(body)
res = req.get_response(nova.api.API())
self.assertEqual(res.status_int, 200)
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
res = req.get_response(nova.api.API())
self.assertEqual(res.status_int, 422)
def test_update_bad_params(self):
""" Confirm that update is filtering params """
inst_dict = dict(cat='leopard', name='server_test', adminPass='bacon')
self.body = json.dumps(dict(server=inst_dict))
def server_update(context, id, params):
self.update_called = True
filtered_dict = dict(name='server_test', admin_pass='bacon')
self.assertEqual(params, filtered_dict)
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(nova.api.API())
def test_update_server(self):
inst_dict = dict(name='server_test', adminPass='bacon')
self.body = json.dumps(dict(server=inst_dict))
def server_update(context, id, params):
filtered_dict = dict(name='server_test', admin_pass='bacon')
self.assertEqual(params, filtered_dict)
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(nova.api.API())
def test_create_backup_schedules(self):
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')

View File

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

View File

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

View 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)

View File

@@ -28,26 +28,71 @@ from nova.api.ec2 import cloud
FLAGS = flags.FLAGS
class user_generator(object):
def __init__(self, manager, **user_state):
if 'name' not in user_state:
user_state['name'] = 'test1'
self.manager = manager
self.user = manager.create_user(**user_state)
class AuthTestCase(test.TrialTestCase):
flush_db = False
def __enter__(self):
return self.user
def __exit__(self, value, type, trace):
self.manager.delete_user(self.user)
class project_generator(object):
def __init__(self, manager, **project_state):
if 'name' not in project_state:
project_state['name'] = 'testproj'
if 'manager_user' not in project_state:
project_state['manager_user'] = 'test1'
self.manager = manager
self.project = manager.create_project(**project_state)
def __enter__(self):
return self.project
def __exit__(self, value, type, trace):
self.manager.delete_project(self.project)
class user_and_project_generator(object):
def __init__(self, manager, user_state={}, project_state={}):
self.manager = manager
if 'name' not in user_state:
user_state['name'] = 'test1'
if 'name' not in project_state:
project_state['name'] = 'testproj'
if 'manager_user' not in project_state:
project_state['manager_user'] = 'test1'
self.user = manager.create_user(**user_state)
self.project = manager.create_project(**project_state)
def __enter__(self):
return (self.user, self.project)
def __exit__(self, value, type, trace):
self.manager.delete_user(self.user)
self.manager.delete_project(self.project)
class AuthManagerTestCase(test.TrialTestCase):
def setUp(self):
super(AuthTestCase, self).setUp()
super(AuthManagerTestCase, self).setUp()
self.flags(connection_type='fake')
self.manager = manager.AuthManager()
def test_001_can_create_users(self):
self.manager.create_user('test1', 'access', 'secret')
self.manager.create_user('test2')
def test_create_and_find_user(self):
with user_generator(self.manager):
self.assert_(self.manager.get_user('test1'))
def test_002_can_get_user(self):
user = self.manager.get_user('test1')
def test_003_can_retreive_properties(self):
user = self.manager.get_user('test1')
self.assertEqual('test1', user.id)
self.assertEqual('access', user.access)
self.assertEqual('secret', user.secret)
def test_create_and_find_with_properties(self):
with user_generator(self.manager, name="herbert", secret="classified",
access="private-party"):
u = self.manager.get_user('herbert')
self.assertEqual('herbert', u.id)
self.assertEqual('herbert', u.name)
self.assertEqual('classified', u.secret)
self.assertEqual('private-party', u.access)
def test_004_signature_is_valid(self):
#self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? ))
@@ -64,133 +109,216 @@ class AuthTestCase(test.TrialTestCase):
'export S3_URL="http://127.0.0.1:3333/"\n' +
'export EC2_USER_ID="test1"\n')
def test_010_can_list_users(self):
users = self.manager.get_users()
logging.warn(users)
self.assertTrue(filter(lambda u: u.id == 'test1', users))
def test_can_list_users(self):
with user_generator(self.manager):
with user_generator(self.manager, name="test2"):
users = self.manager.get_users()
self.assert_(filter(lambda u: u.id == 'test1', users))
self.assert_(filter(lambda u: u.id == 'test2', users))
self.assert_(not filter(lambda u: u.id == 'test3', users))
def test_101_can_add_user_role(self):
self.assertFalse(self.manager.has_role('test1', 'itsec'))
self.manager.add_role('test1', 'itsec')
self.assertTrue(self.manager.has_role('test1', 'itsec'))
def test_can_add_and_remove_user_role(self):
with user_generator(self.manager):
self.assertFalse(self.manager.has_role('test1', 'itsec'))
self.manager.add_role('test1', 'itsec')
self.assertTrue(self.manager.has_role('test1', 'itsec'))
self.manager.remove_role('test1', 'itsec')
self.assertFalse(self.manager.has_role('test1', 'itsec'))
def test_199_can_remove_user_role(self):
self.assertTrue(self.manager.has_role('test1', 'itsec'))
self.manager.remove_role('test1', 'itsec')
self.assertFalse(self.manager.has_role('test1', 'itsec'))
def test_can_create_and_get_project(self):
with user_and_project_generator(self.manager) as (u,p):
self.assert_(self.manager.get_user('test1'))
self.assert_(self.manager.get_user('test1'))
self.assert_(self.manager.get_project('testproj'))
def test_201_can_create_project(self):
project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1'])
self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
self.assertEqual(project.name, 'testproj')
self.assertEqual(project.description, 'A test project')
self.assertEqual(project.project_manager_id, 'test1')
self.assertTrue(project.has_member('test1'))
def test_can_list_projects(self):
with user_and_project_generator(self.manager):
with project_generator(self.manager, name="testproj2"):
projects = self.manager.get_projects()
self.assert_(filter(lambda p: p.name == 'testproj', projects))
self.assert_(filter(lambda p: p.name == 'testproj2', projects))
self.assert_(not filter(lambda p: p.name == 'testproj3',
projects))
def test_202_user1_is_project_member(self):
self.assertTrue(self.manager.get_user('test1').is_project_member('testproj'))
def test_can_create_and_get_project_with_attributes(self):
with user_generator(self.manager):
with project_generator(self.manager, description='A test project'):
project = self.manager.get_project('testproj')
self.assertEqual('A test project', project.description)
def test_203_user2_is_not_project_member(self):
self.assertFalse(self.manager.get_user('test2').is_project_member('testproj'))
def test_can_create_project_with_manager(self):
with user_and_project_generator(self.manager) as (user, project):
self.assertEqual('test1', project.project_manager_id)
self.assertTrue(self.manager.is_project_manager(user, project))
def test_204_user1_is_project_manager(self):
self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj'))
def test_create_project_assigns_manager_to_members(self):
with user_and_project_generator(self.manager) as (user, project):
self.assertTrue(self.manager.is_project_member(user, project))
def test_205_user2_is_not_project_manager(self):
self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj'))
def test_no_extra_project_members(self):
with user_generator(self.manager, name='test2') as baduser:
with user_and_project_generator(self.manager) as (user, project):
self.assertFalse(self.manager.is_project_member(baduser,
project))
def test_206_can_add_user_to_project(self):
self.manager.add_to_project('test2', 'testproj')
self.assertTrue(self.manager.get_project('testproj').has_member('test2'))
def test_no_extra_project_managers(self):
with user_generator(self.manager, name='test2') as baduser:
with user_and_project_generator(self.manager) as (user, project):
self.assertFalse(self.manager.is_project_manager(baduser,
project))
def test_207_can_remove_user_from_project(self):
self.manager.remove_from_project('test2', 'testproj')
self.assertFalse(self.manager.get_project('testproj').has_member('test2'))
def test_can_add_user_to_project(self):
with user_generator(self.manager, name='test2') as user:
with user_and_project_generator(self.manager) as (_user, project):
self.manager.add_to_project(user, project)
project = self.manager.get_project('testproj')
self.assertTrue(self.manager.is_project_member(user, project))
def test_208_can_remove_add_user_with_role(self):
self.manager.add_to_project('test2', 'testproj')
self.manager.add_role('test2', 'developer', 'testproj')
self.manager.remove_from_project('test2', 'testproj')
self.assertFalse(self.manager.has_role('test2', 'developer', 'testproj'))
self.manager.add_to_project('test2', 'testproj')
self.manager.remove_from_project('test2', 'testproj')
def test_can_remove_user_from_project(self):
with user_generator(self.manager, name='test2') as user:
with user_and_project_generator(self.manager) as (_user, project):
self.manager.add_to_project(user, project)
project = self.manager.get_project('testproj')
self.assertTrue(self.manager.is_project_member(user, project))
self.manager.remove_from_project(user, project)
project = self.manager.get_project('testproj')
self.assertFalse(self.manager.is_project_member(user, project))
def test_209_can_generate_x509(self):
# MUST HAVE RUN CLOUD SETUP BY NOW
self.cloud = cloud.CloudController()
self.cloud.setup()
_key, cert_str = self.manager._generate_x509_cert('test1', 'testproj')
logging.debug(cert_str)
def test_can_add_remove_user_with_role(self):
with user_generator(self.manager, name='test2') as user:
with user_and_project_generator(self.manager) as (_user, project):
# NOTE(todd): after modifying users you must reload project
self.manager.add_to_project(user, project)
project = self.manager.get_project('testproj')
self.manager.add_role(user, 'developer', project)
self.assertTrue(self.manager.is_project_member(user, project))
self.manager.remove_from_project(user, project)
project = self.manager.get_project('testproj')
self.assertFalse(self.manager.has_role(user, 'developer',
project))
self.assertFalse(self.manager.is_project_member(user, project))
# Need to verify that it's signed by the right intermediate CA
full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
cloud_cert = crypto.fetch_ca()
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
signed_cert = X509.load_cert_string(cert_str)
chain_cert = X509.load_cert_string(full_chain)
int_cert = X509.load_cert_string(int_cert)
cloud_cert = X509.load_cert_string(cloud_cert)
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
def test_can_generate_x509(self):
# NOTE(todd): this doesn't assert against the auth manager
# so it probably belongs in crypto_unittest
# but I'm leaving it where I found it.
with user_and_project_generator(self.manager) as (user, project):
# NOTE(todd): Should mention why we must setup controller first
# (somebody please clue me in)
cloud_controller = cloud.CloudController()
cloud_controller.setup()
_key, cert_str = self.manager._generate_x509_cert('test1',
'testproj')
logging.debug(cert_str)
if not FLAGS.use_intermediate_ca:
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
else:
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
# Need to verify that it's signed by the right intermediate CA
full_chain = crypto.fetch_ca(project_id='testproj', chain=True)
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
cloud_cert = crypto.fetch_ca()
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
signed_cert = X509.load_cert_string(cert_str)
chain_cert = X509.load_cert_string(full_chain)
int_cert = X509.load_cert_string(int_cert)
cloud_cert = X509.load_cert_string(cloud_cert)
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
if not FLAGS.use_intermediate_ca:
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
else:
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
def test_210_can_add_project_role(self):
project = self.manager.get_project('testproj')
self.assertFalse(project.has_role('test1', 'sysadmin'))
self.manager.add_role('test1', 'sysadmin')
self.assertFalse(project.has_role('test1', 'sysadmin'))
project.add_role('test1', 'sysadmin')
self.assertTrue(project.has_role('test1', 'sysadmin'))
def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
with user_and_project_generator(self.manager) as (user, project):
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
self.manager.add_role(user, 'sysadmin', project)
# NOTE(todd): it will still show up in get_user_roles(u, project)
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
self.manager.add_role(user, 'sysadmin')
self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
def test_211_can_list_project_roles(self):
project = self.manager.get_project('testproj')
user = self.manager.get_user('test1')
self.manager.add_role(user, 'netadmin', project)
roles = self.manager.get_user_roles(user)
self.assertTrue('sysadmin' in roles)
self.assertFalse('netadmin' in roles)
project_roles = self.manager.get_user_roles(user, project)
self.assertTrue('sysadmin' in project_roles)
self.assertTrue('netadmin' in project_roles)
# has role should be false because global role is missing
self.assertFalse(self.manager.has_role(user, 'netadmin', project))
def test_add_user_role_doesnt_infect_project_roles(self):
with user_and_project_generator(self.manager) as (user, project):
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
self.manager.add_role(user, 'sysadmin')
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
def test_can_list_user_roles(self):
with user_and_project_generator(self.manager) as (user, project):
self.manager.add_role(user, 'sysadmin')
roles = self.manager.get_user_roles(user)
self.assertTrue('sysadmin' in roles)
self.assertFalse('netadmin' in roles)
def test_212_can_remove_project_role(self):
project = self.manager.get_project('testproj')
self.assertTrue(project.has_role('test1', 'sysadmin'))
project.remove_role('test1', 'sysadmin')
self.assertFalse(project.has_role('test1', 'sysadmin'))
self.manager.remove_role('test1', 'sysadmin')
self.assertFalse(project.has_role('test1', 'sysadmin'))
def test_can_list_project_roles(self):
with user_and_project_generator(self.manager) as (user, project):
self.manager.add_role(user, 'sysadmin')
self.manager.add_role(user, 'sysadmin', project)
self.manager.add_role(user, 'netadmin', project)
project_roles = self.manager.get_user_roles(user, project)
self.assertTrue('sysadmin' in project_roles)
self.assertTrue('netadmin' in project_roles)
# has role should be false user-level role is missing
self.assertFalse(self.manager.has_role(user, 'netadmin', project))
def test_214_can_retrieve_project_by_user(self):
project = self.manager.create_project('testproj2', 'test2', 'Another test project', ['test2'])
self.assert_(len(self.manager.get_projects()) > 1)
self.assertEqual(len(self.manager.get_projects('test2')), 1)
def test_can_remove_user_roles(self):
with user_and_project_generator(self.manager) as (user, project):
self.manager.add_role(user, 'sysadmin')
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
self.manager.remove_role(user, 'sysadmin')
self.assertFalse(self.manager.has_role(user, 'sysadmin'))
def test_220_can_modify_project(self):
self.manager.modify_project('testproj', 'test2', 'new description')
project = self.manager.get_project('testproj')
self.assertEqual(project.project_manager_id, 'test2')
self.assertEqual(project.description, 'new description')
def test_removing_user_role_hides_it_from_project(self):
with user_and_project_generator(self.manager) as (user, project):
self.manager.add_role(user, 'sysadmin')
self.manager.add_role(user, 'sysadmin', project)
self.assertTrue(self.manager.has_role(user, 'sysadmin', project))
self.manager.remove_role(user, 'sysadmin')
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
def test_299_can_delete_project(self):
self.manager.delete_project('testproj')
self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects()))
self.manager.delete_project('testproj2')
def test_can_remove_project_role_but_keep_user_role(self):
with user_and_project_generator(self.manager) as (user, project):
self.manager.add_role(user, 'sysadmin')
self.manager.add_role(user, 'sysadmin', project)
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
self.manager.remove_role(user, 'sysadmin', project)
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
self.assertTrue(self.manager.has_role(user, 'sysadmin'))
def test_999_can_delete_users(self):
def test_can_retrieve_project_by_user(self):
with user_and_project_generator(self.manager) as (user, project):
self.assertEqual(1, len(self.manager.get_projects('test1')))
def test_can_modify_project(self):
with user_and_project_generator(self.manager):
with user_generator(self.manager, name='test2'):
self.manager.modify_project('testproj', 'test2', 'new desc')
project = self.manager.get_project('testproj')
self.assertEqual('test2', project.project_manager_id)
self.assertEqual('new desc', project.description)
def test_can_delete_project(self):
with user_generator(self.manager):
self.manager.create_project('testproj', 'test1')
self.assert_(self.manager.get_project('testproj'))
self.manager.delete_project('testproj')
projectlist = self.manager.get_projects()
self.assert_(not filter(lambda p: p.name == 'testproj',
projectlist))
def test_can_delete_user(self):
self.manager.create_user('test1')
self.assert_(self.manager.get_user('test1'))
self.manager.delete_user('test1')
users = self.manager.get_users()
self.assertFalse(filter(lambda u: u.id == 'test1', users))
self.manager.delete_user('test2')
self.assertEqual(self.manager.get_user('test2'), None)
userlist = self.manager.get_users()
self.assert_(not filter(lambda u: u.id == 'test1', userlist))
def test_can_modify_users(self):
with user_generator(self.manager):
self.manager.modify_user('test1', 'access', 'secret', True)
user = self.manager.get_user('test1')
self.assertEqual('access', user.access)
self.assertEqual('secret', user.secret)
self.assertTrue(user.is_admin())
if __name__ == "__main__":

View File

@@ -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'])

View File

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

View File

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

View File

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