summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@yahoo.com>2010-10-13 23:17:40 -0700
committerVishvananda Ishaya <vishvananda@yahoo.com>2010-10-13 23:17:40 -0700
commit7403ece82902e633fbd3f2e6f0303ad08c269541 (patch)
tree7372b04b62216a2d73e4c10dbc8eec8799ea4925
parentdef93d479f3a829c9e1bc0a4c3516ee881796456 (diff)
parent134b846d23be923f7453e945e92f32dffbc54f50 (diff)
merged trunk
-rwxr-xr-xbin/nova-manage31
-rw-r--r--nova/api/ec2/cloud.py26
-rw-r--r--nova/api/openstack/auth.py42
-rw-r--r--nova/api/openstack/servers.py35
-rw-r--r--nova/auth/manager.py15
-rw-r--r--nova/compute/manager.py20
-rw-r--r--nova/db/api.py79
-rw-r--r--nova/db/sqlalchemy/api.py120
-rw-r--r--nova/db/sqlalchemy/models.py31
-rw-r--r--nova/fakerabbit.py1
-rw-r--r--nova/network/linux_net.py29
-rw-r--r--nova/network/manager.py286
-rw-r--r--nova/test.py18
-rw-r--r--nova/tests/api/openstack/fakes.py13
-rw-r--r--nova/tests/api/openstack/test_auth.py10
-rw-r--r--nova/tests/cloud_unittest.py5
-rw-r--r--nova/tests/compute_unittest.py3
-rw-r--r--nova/tests/network_unittest.py32
-rw-r--r--nova/tests/scheduler_unittest.py1
-rw-r--r--nova/tests/virt_unittest.py16
-rw-r--r--nova/virt/fake.py2
-rw-r--r--nova/virt/libvirt.xen.xml.template30
-rw-r--r--nova/virt/libvirt_conn.py53
-rw-r--r--nova/virt/xenapi.py3
24 files changed, 523 insertions, 378 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index e19bf70b7..d36b0f53a 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -73,6 +73,7 @@ from nova import flags
from nova import quota
from nova import utils
from nova.auth import manager
+from nova.network import manager as network_manager
from nova.cloudpipe import pipelib
@@ -377,6 +378,29 @@ class FloatingIpCommands(object):
floating_ip['address'],
instance)
+class NetworkCommands(object):
+ """Class for managing networks."""
+
+ def create(self, fixed_range=None, num_networks=None,
+ network_size=None, vlan_start=None, vpn_start=None):
+ """Creates fixed ips for host by range
+ arguments: [fixed_range=FLAG], [num_networks=FLAG],
+ [network_size=FLAG], [vlan_start=FLAG],
+ [vpn_start=FLAG]"""
+ if not fixed_range:
+ fixed_range = FLAGS.fixed_range
+ if not num_networks:
+ num_networks = FLAGS.num_networks
+ if not network_size:
+ network_size = FLAGS.network_size
+ if not vlan_start:
+ vlan_start = FLAGS.vlan_start
+ if not vpn_start:
+ vpn_start = FLAGS.vpn_start
+ net_manager = utils.import_object(FLAGS.network_manager)
+ net_manager.create_networks(None, fixed_range, int(num_networks),
+ int(network_size), int(vlan_start),
+ int(vpn_start))
CATEGORIES = [
('user', UserCommands),
@@ -384,7 +408,8 @@ CATEGORIES = [
('role', RoleCommands),
('shell', ShellCommands),
('vpn', VpnCommands),
- ('floating', FloatingIpCommands)
+ ('floating', FloatingIpCommands),
+ ('network', NetworkCommands)
]
@@ -454,9 +479,9 @@ def main():
fn(*argv)
sys.exit(0)
except TypeError:
- print "Wrong number of arguments supplied"
+ print "Possible wrong number of arguments supplied"
print "%s %s: %s" % (category, action, fn.__doc__)
- sys.exit(2)
+ raise
if __name__ == '__main__':
main()
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 4e05f8252..56bf2db03 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -452,11 +452,16 @@ class CloudController(object):
ec2_id = instance_id[0]
internal_id = ec2_id_to_internal_id(ec2_id)
instance_ref = db.instance_get_by_internal_id(context, internal_id)
- return rpc.call('%s.%s' % (FLAGS.compute_topic,
- instance_ref['host']),
- {"method": "get_console_output",
- "args": {"context": None,
- "instance_id": instance_ref['id']}})
+ output = rpc.call('%s.%s' % (FLAGS.compute_topic,
+ instance_ref['host']),
+ { "method" : "get_console_output",
+ "args" : { "context": None,
+ "instance_id": instance_ref['id']}})
+
+ now = datetime.datetime.utcnow()
+ return { "InstanceId" : ec2_id,
+ "Timestamp" : now,
+ "output" : base64.b64encode(output) }
def describe_volumes(self, context, **kwargs):
if context.user.is_admin():
@@ -734,13 +739,13 @@ class CloudController(object):
def _get_network_topic(self, context):
"""Retrieves the network host for a project"""
- network_ref = db.project_get_network(context, context.project.id)
+ network_ref = self.network_manager.get_network(context)
host = network_ref['host']
if not host:
host = rpc.call(FLAGS.network_topic,
{"method": "set_network_host",
"args": {"context": None,
- "project_id": context.project.id}})
+ "network_id": network_ref['id']}})
return db.queue_get_for(context, FLAGS.network_topic, host)
def _ensure_default_security_group(self, context):
@@ -851,12 +856,13 @@ class CloudController(object):
ec2_id = internal_id_to_ec2_id(internal_id)
inst['hostname'] = ec2_id
db.instance_update(context, inst_id, inst)
+ # TODO(vish): This probably should be done in the scheduler
+ # or in compute as a call. The network should be
+ # allocated after the host is assigned and setup
+ # can happen at the same time.
address = self.network_manager.allocate_fixed_ip(context,
inst_id,
vpn)
-
- # TODO(vish): This probably should be done in the scheduler
- # network is setup when host is assigned
network_topic = self._get_network_topic(context)
rpc.call(network_topic,
{"method": "setup_fixed_ip",
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 4c909293e..7aba55728 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -24,9 +24,9 @@ class BasicApiAuthManager(object):
def __init__(self, host=None, db_driver=None):
if not host:
host = FLAGS.host
- self.host = host
+ self.host = host
if not db_driver:
- db_driver = FLAGS.db_driver
+ db_driver = FLAGS.db_driver
self.db = utils.import_object(db_driver)
self.auth = auth.manager.AuthManager()
self.context = Context()
@@ -40,20 +40,19 @@ class BasicApiAuthManager(object):
return faults.Fault(webob.exc.HTTPUnauthorized())
try:
- username, key = req.headers['X-Auth-User'], \
- req.headers['X-Auth-Key']
+ username = req.headers['X-Auth-User']
+ key = req.headers['X-Auth-Key']
except KeyError:
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)
if user and token:
res = webob.Response()
- res.headers['X-Auth-Token'] = token['token_hash']
+ res.headers['X-Auth-Token'] = token.token_hash
res.headers['X-Server-Management-Url'] = \
- token['server_management_url']
- res.headers['X-Storage-Url'] = token['storage_url']
- res.headers['X-CDN-Management-Url'] = token['cdn_management_url']
+ token.server_management_url
+ res.headers['X-Storage-Url'] = token.storage_url
+ res.headers['X-CDN-Management-Url'] = token.cdn_management_url
res.content_type = 'text/plain'
res.status = '204'
return res
@@ -65,34 +64,35 @@ class BasicApiAuthManager(object):
If the token has expired, returns None
If the token is not found, returns None
- Otherwise returns the token
+ Otherwise returns dict(id=(the authorized user's id))
This method will also remove the token if the timestamp is older than
2 days ago.
"""
token = self.db.auth_get_token(self.context, token_hash)
if token:
- delta = datetime.datetime.now() - token['created_at']
+ delta = datetime.datetime.now() - token.created_at
if delta.days >= 2:
self.db.auth_destroy_token(self.context, token)
else:
- user = self.auth.get_user(token['user_id'])
- return { 'id':user['uid'] }
+ #TODO(gundlach): Why not just return dict(id=token.user_id)?
+ user = self.auth.get_user(token.user_id)
+ return {'id': user.id}
return None
def _authorize_user(self, username, key):
""" Generates a new token and assigns it to a user """
user = self.auth.get_user_from_access_key(key)
- if user and user['name'] == username:
+ if user and user.name == username:
token_hash = hashlib.sha1('%s%s%f' % (username, key,
time.time())).hexdigest()
- token = {}
- token['token_hash'] = token_hash
- token['cdn_management_url'] = ''
- token['server_management_url'] = self._get_server_mgmt_url()
- token['storage_url'] = ''
- token['user_id'] = user['uid']
- self.db.auth_create_token(self.context, token)
+ token_dict = {}
+ token_dict['token_hash'] = token_hash
+ token_dict['cdn_management_url'] = ''
+ token_dict['server_management_url'] = self._get_server_mgmt_url()
+ token_dict['storage_url'] = ''
+ token_dict['user_id'] = user.id
+ token = self.db.auth_create_token(self.context, token_dict)
return token, user
return None, None
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 5d1ed9822..1a0792bf8 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -48,9 +48,9 @@ def _entity_list(entities):
return dict(servers=entities)
def _entity_detail(inst):
- """ Maps everything to valid attributes for return"""
- power_mapping = {
- power_state.NOSTATE: 'build',
+ """ Maps everything to Rackspace-like attributes for return"""
+ power_mapping = {
+ power_state.NOSTATE: 'build',
power_state.RUNNING: 'active',
power_state.BLOCKED: 'active',
power_state.PAUSED: 'suspended',
@@ -60,7 +60,7 @@ def _entity_detail(inst):
}
inst_dict = {}
- mapped_keys = dict(status='state', imageId='image_id',
+ mapped_keys = dict(status='state', imageId='image_id',
flavorId='instance_type', name='server_name', id='id')
for k, v in mapped_keys.iteritems():
@@ -83,7 +83,7 @@ class Controller(wsgi.Controller):
_serialization_metadata = {
'application/xml': {
"attributes": {
- "server": [ "id", "imageId", "name", "flavorId", "hostId",
+ "server": [ "id", "imageId", "name", "flavorId", "hostId",
"status", "progress", "progress" ]
}
}
@@ -155,7 +155,7 @@ class Controller(wsgi.Controller):
user_id = req.environ['nova.context']['user']['id']
inst_dict = self._deserialize(req.body, req)
-
+
if not inst_dict:
return faults.Fault(exc.HTTPUnprocessableEntity())
@@ -163,12 +163,12 @@ class Controller(wsgi.Controller):
if not instance or instance.user_id != user_id:
return faults.Fault(exc.HTTPNotFound())
- self.db_driver.instance_update(None, int(id),
+ self.db_driver.instance_update(None, int(id),
_filter_params(inst_dict['server']))
return faults.Fault(exc.HTTPNoContent())
def action(self, req, id):
- """ multi-purpose method used to reboot, rebuild, and
+ """ multi-purpose method used to reboot, rebuild, and
resize a server """
user_id = req.environ['nova.context']['user']['id']
input_dict = self._deserialize(req.body, req)
@@ -195,12 +195,11 @@ class Controller(wsgi.Controller):
if v['flavorid'] == flavor_id][0]
image_id = env['server']['imageId']
-
img_service = utils.import_object(FLAGS.image_service)
image = img_service.show(image_id)
- if not image:
+ if not image:
raise Exception, "Image not found"
inst['server_name'] = env['server']['name']
@@ -236,15 +235,14 @@ class Controller(wsgi.Controller):
ref = self.db_driver.instance_create(None, inst)
inst['id'] = ref.internal_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?
+
+ #TODO(dietz) is this necessary?
inst['launch_index'] = 0
inst['hostname'] = str(ref.internal_id)
@@ -256,21 +254,20 @@ class Controller(wsgi.Controller):
# 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)
+ network_topic = self._get_network_topic(None)
rpc.call(network_topic,
{"method": "setup_fixed_ip",
"args": {"context": None,
"address": address}})
return inst
- def _get_network_topic(self, user_id):
+ def _get_network_topic(self, context):
"""Retrieves the network host for a project"""
- network_ref = self.db_driver.project_get_network(None,
- user_id)
+ network_ref = self.network_manager.get_network(context)
host = network_ref['host']
if not host:
host = rpc.call(FLAGS.network_topic,
{"method": "set_network_host",
"args": {"context": None,
- "project_id": user_id}})
+ "network_id": network_ref['id']}})
return self.db_driver.queue_get_for(None, FLAGS.network_topic, host)
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index 58e33969b..9c499c98d 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -484,13 +484,6 @@ class AuthManager(object):
member_users)
if project_dict:
project = Project(**project_dict)
- try:
- self.network_manager.allocate_network(context,
- project.id)
- except:
- drv.delete_project(project.id)
- raise
-
return project
def modify_project(self, project, manager_user=None, description=None):
@@ -559,14 +552,6 @@ class AuthManager(object):
def delete_project(self, project, context=None):
"""Deletes a project"""
- try:
- network_ref = db.project_get_network(context,
- Project.safe_id(project))
- db.network_destroy(context, network_ref['id'])
- except:
- logging.exception('Could not destroy network for %s',
- project)
-
with self.driver() as drv:
drv.delete_project(Project.safe_id(project))
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index c5ae5e3d9..94c95038f 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -76,7 +76,7 @@ class ComputeManager(manager.Manager):
raise exception.Error("Instance has already been created")
logging.debug("instance %s: starting...", instance_id)
project_id = instance_ref['project_id']
- self.network_manager.setup_compute_network(context, project_id)
+ self.network_manager.setup_compute_network(context, instance_id)
self.db.instance_update(context,
instance_id,
{'host': self.host})
@@ -145,26 +145,10 @@ class ComputeManager(manager.Manager):
@exception.wrap_exception
def get_console_output(self, context, instance_id):
"""Send the console output for an instance."""
- # TODO(vish): Move this into the driver layer
-
logging.debug("instance %s: getting console output", instance_id)
instance_ref = self.db.instance_get(context, instance_id)
- if FLAGS.connection_type == 'libvirt':
- fname = os.path.abspath(os.path.join(FLAGS.instances_path,
- instance_ref['internal_id'],
- 'console.log'))
- with open(fname, 'r') as f:
- output = f.read()
- else:
- output = 'FAKE CONSOLE OUTPUT'
-
- # TODO(termie): this stuff belongs in the API layer, no need to
- # munge the data we send to ourselves
- output = {"InstanceId": instance_id,
- "Timestamp": "2",
- "output": base64.b64encode(output)}
- return output
+ return self.driver.get_console_output(instance_ref)
@defer.inlineCallbacks
@exception.wrap_exception
diff --git a/nova/db/api.py b/nova/db/api.py
index 8bf5198d9..6dbf3b809 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -258,7 +258,7 @@ def instance_get_all(context):
def instance_get_all_by_user(context, user_id):
"""Get all instances."""
- return IMPL.instance_get_all(context, user_id)
+ return IMPL.instance_get_all_by_user(context, user_id)
def instance_get_all_by_project(context, project_id):
"""Get all instance belonging to a project."""
@@ -340,6 +340,11 @@ def key_pair_get_all_by_user(context, user_id):
####################
+def network_associate(context, project_id):
+ """Associate a free network to a project."""
+ return IMPL.network_associate(context, project_id)
+
+
def network_count(context):
"""Return the number of networks."""
return IMPL.network_count(context)
@@ -360,9 +365,12 @@ def network_count_reserved_ips(context, network_id):
return IMPL.network_count_reserved_ips(context, network_id)
-def network_create(context, values):
- """Create a network from the values dictionary."""
- return IMPL.network_create(context, values)
+def network_create_safe(context, values):
+ """Create a network from the values dict
+
+ The network is only returned if the create succeeds. If the create violates
+ constraints because the network already exists, no exception is raised."""
+ return IMPL.network_create_safe(context, values)
def network_create_fixed_ips(context, network_id, num_vpn_clients):
@@ -370,9 +378,14 @@ def network_create_fixed_ips(context, network_id, num_vpn_clients):
return IMPL.network_create_fixed_ips(context, network_id, num_vpn_clients)
-def network_destroy(context, network_id):
- """Destroy the network or raise if it does not exist."""
- return IMPL.network_destroy(context, network_id)
+def network_disassociate(context, network_id):
+ """Disassociate the network from project or raise if it does not exist."""
+ return IMPL.network_disassociate(context, network_id)
+
+
+def network_disassociate_all(context):
+ """Disassociate all networks from projects."""
+ return IMPL.network_disassociate_all(context)
def network_get(context, network_id):
@@ -387,10 +400,15 @@ def network_get_associated_fixed_ips(context, network_id):
def network_get_by_bridge(context, bridge):
- """Get an network or raise if it does not exist."""
+ """Get a network by bridge or raise if it does not exist."""
return IMPL.network_get_by_bridge(context, bridge)
+def network_get_by_instance(context, instance_id):
+ """Get a network by instance id or raise if it does not exist."""
+ return IMPL.network_get_by_instance(context, instance_id)
+
+
def network_get_index(context, network_id):
"""Get non-conflicting index for network"""
return IMPL.network_get_index(context, network_id)
@@ -401,19 +419,6 @@ def network_get_vpn_ip(context, network_id):
return IMPL.network_get_vpn_ip(context, network_id)
-def network_index_count(context):
- """Return count of network indexes"""
- return IMPL.network_index_count(context)
-
-
-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):
"""Set the Classless Inner Domain Routing for the network"""
return IMPL.network_set_cidr(context, network_id, cidr)
@@ -473,6 +478,22 @@ def export_device_create_safe(context, values):
###################
+def auth_destroy_token(context, token):
+ """Destroy an auth token"""
+ return IMPL.auth_destroy_token(context, token)
+
+def auth_get_token(context, token_hash):
+ """Retrieves a token given the hash representing it"""
+ return IMPL.auth_get_token(context, token_hash)
+
+def auth_create_token(context, token):
+ """Creates a new token"""
+ return IMPL.auth_create_token(context, token)
+
+
+###################
+
+
def quota_create(context, values):
"""Create a quota from the values dictionary."""
return IMPL.quota_create(context, values)
@@ -496,22 +517,6 @@ def quota_destroy(context, project_id):
###################
-def auth_destroy_token(context, token):
- """Destroy an auth token"""
- return IMPL.auth_destroy_token(context, token)
-
-def auth_get_token(context, token_hash):
- """Retrieves a token given the hash representing it"""
- return IMPL.auth_get_token(context, token_hash)
-
-def auth_create_token(context, token):
- """Creates a new token"""
- return IMPL.auth_create_token(context, token_hash, token)
-
-
-###################
-
-
def volume_allocate_shelf_and_blade(context, volume_id):
"""Atomically allocate a free shelf and blade from the pool."""
return IMPL.volume_allocate_shelf_and_blade(context, volume_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index c0f656c9f..f4a746cab 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -804,6 +804,24 @@ def key_pair_get_all_by_user(context, user_id):
@require_admin_context
+def network_associate(context, project_id):
+ session = get_session()
+ with session.begin():
+ network_ref = session.query(models.Network
+ ).filter_by(deleted=False
+ ).filter_by(project_id=None
+ ).with_lockmode('update'
+ ).first()
+ # NOTE(vish): if with_lockmode isn't supported, as in sqlite,
+ # then this has concurrency issues
+ if not network_ref:
+ raise db.NoMoreNetworks()
+ network_ref['project_id'] = project_id
+ session.add(network_ref)
+ return network_ref
+
+
+@require_admin_context
def network_count(context):
session = get_session()
return session.query(models.Network
@@ -843,31 +861,26 @@ def network_count_reserved_ips(context, network_id):
@require_admin_context
-def network_create(context, values):
+def network_create_safe(context, values):
network_ref = models.Network()
for (key, value) in values.iteritems():
network_ref[key] = value
- network_ref.save()
- return network_ref
+ try:
+ network_ref.save()
+ return network_ref
+ except IntegrityError:
+ return None
+
+
+@require_admin_context
+def network_disassociate(context, network_id):
+ network_update(context, network_id, {'project_id': None})
@require_admin_context
-def network_destroy(context, network_id):
+def network_disassociate_all(context):
session = get_session()
- with session.begin():
- # TODO(vish): do we have to use sql here?
- session.execute('update networks set deleted=1 where id=:id',
- {'id': network_id})
- session.execute('update fixed_ips set deleted=1 where network_id=:id',
- {'id': network_id})
- session.execute('update floating_ips set deleted=1 '
- 'where fixed_ip_id in '
- '(select id from fixed_ips '
- 'where network_id=:id)',
- {'id': network_id})
- session.execute('update network_indexes set network_id=NULL '
- 'where network_id=:id',
- {'id': network_id})
+ session.execute('update networks set project_id=NULL')
@require_context
@@ -917,48 +930,21 @@ def network_get_by_bridge(context, bridge):
if not result:
raise exception.NotFound('No network for bridge %s' % bridge)
-
return result
@require_admin_context
-def network_get_index(context, network_id):
+def network_get_by_instance(_context, instance_id):
session = get_session()
- with session.begin():
- network_index = session.query(models.NetworkIndex
- ).filter_by(network_id=None
- ).filter_by(deleted=False
- ).with_lockmode('update'
- ).first()
-
- if not network_index:
- raise db.NoMoreNetworks()
-
- network_index['network'] = network_get(context,
- network_id,
- session=session)
- session.add(network_index)
-
- return network_index['index']
-
-
-@require_admin_context
-def network_index_count(context):
- session = get_session()
- return session.query(models.NetworkIndex
- ).filter_by(deleted=can_read_deleted(context)
- ).count()
-
-
-@require_admin_context
-def network_index_create_safe(context, values):
- network_index_ref = models.NetworkIndex()
- for (key, value) in values.iteritems():
- network_index_ref[key] = value
- try:
- network_index_ref.save()
- except IntegrityError:
- pass
+ rv = session.query(models.Network
+ ).filter_by(deleted=False
+ ).join(models.Network.fixed_ips
+ ).filter_by(instance_id=instance_id
+ ).filter_by(deleted=False
+ ).first()
+ if not rv:
+ raise exception.NotFound('No network for instance %s' % instance_id)
+ return rv
@require_admin_context
@@ -998,15 +984,22 @@ def network_update(context, network_id, values):
@require_context
def project_get_network(context, project_id):
session = get_session()
- result= session.query(models.Network
+ rv = session.query(models.Network
).filter_by(project_id=project_id
).filter_by(deleted=False
).first()
-
- if not result:
- raise exception.NotFound('No network for project: %s' % project_id)
-
- return result
+ if not rv:
+ try:
+ return network_associate(context, project_id)
+ except IntegrityError:
+ # NOTE(vish): We hit this if there is a race and two
+ # processes are attempting to allocate the
+ # network at the same time
+ rv = session.query(models.Network
+ ).filter_by(project_id=project_id
+ ).filter_by(deleted=False
+ ).first()
+ return rv
###################
@@ -1050,7 +1043,8 @@ def auth_destroy_token(_context, token):
def auth_get_token(_context, token_hash):
session = get_session()
tk = session.query(models.AuthToken
- ).filter_by(token_hash=token_hash)
+ ).filter_by(token_hash=token_hash
+ ).first()
if not tk:
raise exception.NotFound('Token %s does not exist' % token_hash)
return tk
@@ -1493,7 +1487,7 @@ def user_get_by_access_key(context, access_key, session=None):
).first()
if not result:
- raise exception.NotFound('No user for id %s' % id)
+ raise exception.NotFound('No user for access key %s' % access_key)
return result
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index d81de3d2f..a63bca2b0 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -364,10 +364,13 @@ class KeyPair(BASE, NovaBase):
class Network(BASE, NovaBase):
"""Represents a network"""
__tablename__ = 'networks'
+ __table_args__ = (schema.UniqueConstraint("vpn_public_address",
+ "vpn_public_port"),
+ {'mysql_engine': 'InnoDB'})
id = Column(Integer, primary_key=True)
injected = Column(Boolean, default=False)
- cidr = Column(String(255))
+ cidr = Column(String(255), unique=True)
netmask = Column(String(255))
bridge = Column(String(255))
gateway = Column(String(255))
@@ -380,28 +383,13 @@ class Network(BASE, NovaBase):
vpn_private_address = Column(String(255))
dhcp_start = Column(String(255))
- project_id = Column(String(255))
+ # NOTE(vish): The unique constraint below helps avoid a race condition
+ # when associating a network, but it also means that we
+ # can't associate two networks with one project.
+ project_id = Column(String(255), unique=True)
host = Column(String(255)) # , ForeignKey('hosts.id'))
-class NetworkIndex(BASE, NovaBase):
- """Represents a unique offset for a network
-
- Currently vlan number, vpn port, and fixed ip ranges are keyed off of
- this index. These may ultimately need to be converted to separate
- pools.
- """
- __tablename__ = 'network_indexes'
- id = Column(Integer, primary_key=True)
- index = Column(Integer, unique=True)
- network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
- network = relationship(Network,
- backref=backref('network_index', uselist=False),
- foreign_keys=network_id,
- primaryjoin='and_(NetworkIndex.network_id==Network.id,'
- 'NetworkIndex.deleted==False)')
-
-
class AuthToken(BASE, NovaBase):
"""Represents an authorization token for all API transactions. Fields
are a string representing the actual token and a user id for mapping
@@ -414,7 +402,6 @@ class AuthToken(BASE, NovaBase):
cdn_management_url = Column(String(255))
-
# TODO(vish): can these both come from the same baseclass?
class FixedIp(BASE, NovaBase):
"""Represents a fixed ip for an instance"""
@@ -518,7 +505,7 @@ def register_models():
"""Register Models and create metadata"""
from sqlalchemy import create_engine
models = (Service, Instance, Volume, ExportDevice, FixedIp,
- FloatingIp, Network, NetworkIndex, SecurityGroup,
+ FloatingIp, Network, SecurityGroup,
SecurityGroupIngressRule, SecurityGroupInstanceAssociation,
AuthToken, User, Project) # , Image, Host
engine = create_engine(FLAGS.sql_connection, echo=False)
diff --git a/nova/fakerabbit.py b/nova/fakerabbit.py
index 835973810..df5e61e6e 100644
--- a/nova/fakerabbit.py
+++ b/nova/fakerabbit.py
@@ -116,6 +116,7 @@ class Backend(object):
message = Message(backend=self, body=message_data,
content_type=content_type,
content_encoding=content_encoding)
+ message.result = True
logging.debug('Getting from %s: %s', queue, message)
return message
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 37f9c8253..c0be0e8cc 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -65,12 +65,12 @@ def init_host():
# SNAT rule for outbound traffic.
_confirm_rule("POSTROUTING", "-t nat -s %s "
"-j SNAT --to-source %s"
- % (FLAGS.private_range, FLAGS.routing_source_ip))
+ % (FLAGS.fixed_range, FLAGS.routing_source_ip))
_confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" %
- FLAGS.private_range)
+ FLAGS.fixed_range)
_confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" %
- {'range': FLAGS.private_range})
+ {'range': FLAGS.fixed_range})
def bind_floating_ip(floating_ip):
"""Bind ip to public interface"""
@@ -180,14 +180,14 @@ def update_dhcp(context, network_id):
"""
network_ref = db.network_get(context, network_id)
- conffile = _dhcp_file(network_ref['vlan'], 'conf')
+ conffile = _dhcp_file(network_ref['bridge'], 'conf')
with open(conffile, 'w') as f:
f.write(get_dhcp_hosts(context, network_id))
# Make sure dnsmasq can actually read it (it setuid()s to "nobody")
os.chmod(conffile, 0644)
- pid = _dnsmasq_pid_for(network_ref['vlan'])
+ pid = _dnsmasq_pid_for(network_ref['bridge'])
# if dnsmasq is already running, then tell it to reload
if pid:
@@ -250,11 +250,11 @@ def _dnsmasq_cmd(net):
' --strict-order',
' --bind-interfaces',
' --conf-file=',
- ' --pid-file=%s' % _dhcp_file(net['vlan'], 'pid'),
+ ' --pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),
' --listen-address=%s' % net['gateway'],
' --except-interface=lo',
' --dhcp-range=%s,static,120s' % net['dhcp_start'],
- ' --dhcp-hostsfile=%s' % _dhcp_file(net['vlan'], 'conf'),
+ ' --dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'),
' --dhcp-script=%s' % FLAGS.dhcpbridge,
' --leasefile-ro']
return ''.join(cmd)
@@ -271,24 +271,25 @@ def _stop_dnsmasq(network):
logging.debug("Killing dnsmasq threw %s", exc)
-def _dhcp_file(vlan, kind):
- """Return path to a pid, leases or conf file for a vlan"""
+def _dhcp_file(bridge, kind):
+ """Return path to a pid, leases or conf file for a bridge"""
if not os.path.exists(FLAGS.networks_path):
os.makedirs(FLAGS.networks_path)
+ return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path,
+ bridge,
+ kind))
- return os.path.abspath("%s/nova-%s.%s" % (FLAGS.networks_path, vlan, kind))
-
-def _dnsmasq_pid_for(vlan):
- """Returns he pid for prior dnsmasq instance for a vlan
+def _dnsmasq_pid_for(bridge):
+ """Returns the pid for prior dnsmasq instance for a bridge
Returns None if no pid file exists
If machine has rebooted pid might be incorrect (caller should check)
"""
- pid_file = _dhcp_file(vlan, 'pid')
+ pid_file = _dhcp_file(bridge, 'pid')
if os.path.exists(pid_file):
with open(pid_file, 'r') as f:
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 093a6be9a..2ea1c1aa0 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -37,19 +37,10 @@ from nova import utils
FLAGS = flags.FLAGS
flags.DEFINE_string('flat_network_bridge', 'br100',
'Bridge for simple network instances')
-flags.DEFINE_list('flat_network_ips',
- ['192.168.0.2', '192.168.0.3', '192.168.0.4'],
- 'Available ips for simple network')
-flags.DEFINE_string('flat_network_network', '192.168.0.0',
- 'Network for simple network')
-flags.DEFINE_string('flat_network_netmask', '255.255.255.0',
- 'Netmask for simple network')
-flags.DEFINE_string('flat_network_gateway', '192.168.0.1',
- 'Broadcast for simple network')
-flags.DEFINE_string('flat_network_broadcast', '192.168.0.255',
- 'Broadcast for simple network')
flags.DEFINE_string('flat_network_dns', '8.8.4.4',
'Dns for simple network')
+flags.DEFINE_string('flat_network_dhcp_start', '192.168.0.2',
+ 'Dhcp start for FlatDhcp')
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
flags.DEFINE_integer('num_networks', 1000, 'Number of networks to support')
flags.DEFINE_string('vpn_ip', utils.get_my_ip(),
@@ -57,8 +48,8 @@ flags.DEFINE_string('vpn_ip', utils.get_my_ip(),
flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
flags.DEFINE_integer('network_size', 256,
'Number of addresses in each private subnet')
-flags.DEFINE_string('public_range', '4.4.4.0/24', 'Public IP address block')
-flags.DEFINE_string('private_range', '10.0.0.0/8', 'Private IP address block')
+flags.DEFINE_string('floating_range', '4.4.4.0/24', 'Floating IP address block')
+flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
flags.DEFINE_integer('cnt_vpn_clients', 5,
'Number of addresses reserved for vpn clients')
flags.DEFINE_string('network_driver', 'nova.network.linux_net',
@@ -91,13 +82,9 @@ class NetworkManager(manager.Manager):
for network in self.db.host_get_networks(None, self.host):
self._on_set_network_host(None, network['id'])
- def set_network_host(self, context, project_id):
- """Safely sets the host of the projects network"""
+ def set_network_host(self, context, network_id):
+ """Safely sets the host of the network"""
logging.debug("setting network host")
- network_ref = self.db.project_get_network(context, project_id)
- # TODO(vish): can we minimize db access by just getting the
- # id here instead of the ref?
- network_id = network_ref['id']
host = self.db.network_set_host(None,
network_id,
self.host)
@@ -108,7 +95,7 @@ class NetworkManager(manager.Manager):
"""Gets a fixed ip from the pool"""
raise NotImplementedError()
- def deallocate_fixed_ip(self, context, instance_id, *args, **kwargs):
+ def deallocate_fixed_ip(self, context, address, *args, **kwargs):
"""Returns a fixed ip to the pool"""
raise NotImplementedError()
@@ -117,10 +104,10 @@ class NetworkManager(manager.Manager):
raise NotImplementedError()
def _on_set_network_host(self, context, network_id):
- """Called when this host becomes the host for a project"""
+ """Called when this host becomes the host for a network"""
raise NotImplementedError()
- def setup_compute_network(self, context, project_id):
+ def setup_compute_network(self, context, instance_id):
"""Sets up matching network for compute hosts"""
raise NotImplementedError()
@@ -150,6 +137,57 @@ class NetworkManager(manager.Manager):
"""Returns an floating ip to the pool"""
self.db.floating_ip_deallocate(context, floating_address)
+ def lease_fixed_ip(self, context, mac, address):
+ """Called by dhcp-bridge when ip is leased"""
+ logging.debug("Leasing IP %s", address)
+ fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
+ instance_ref = fixed_ip_ref['instance']
+ if not instance_ref:
+ raise exception.Error("IP %s leased that isn't associated" %
+ address)
+ if instance_ref['mac_address'] != mac:
+ raise exception.Error("IP %s leased to bad mac %s vs %s" %
+ (address, instance_ref['mac_address'], mac))
+ self.db.fixed_ip_update(context,
+ fixed_ip_ref['address'],
+ {'leased': True})
+ if not fixed_ip_ref['allocated']:
+ logging.warn("IP %s leased that was already deallocated", address)
+
+ def release_fixed_ip(self, context, mac, address):
+ """Called by dhcp-bridge when ip is released"""
+ logging.debug("Releasing IP %s", address)
+ fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
+ instance_ref = fixed_ip_ref['instance']
+ if not instance_ref:
+ raise exception.Error("IP %s released that isn't associated" %
+ address)
+ if instance_ref['mac_address'] != mac:
+ raise exception.Error("IP %s released from bad mac %s vs %s" %
+ (address, instance_ref['mac_address'], mac))
+ if not fixed_ip_ref['leased']:
+ logging.warn("IP %s released that was not leased", address)
+ self.db.fixed_ip_update(context,
+ fixed_ip_ref['str_id'],
+ {'leased': False})
+ if not fixed_ip_ref['allocated']:
+ self.db.fixed_ip_disassociate(context, address)
+ # NOTE(vish): dhcp server isn't updated until next setup, this
+ # means there will stale entries in the conf file
+ # the code below will update the file if necessary
+ if FLAGS.update_dhcp_on_disassociate:
+ network_ref = self.db.fixed_ip_get_network(context, address)
+ self.driver.update_dhcp(context, network_ref['id'])
+
+ def get_network(self, context):
+ """Get the network for the current context"""
+ raise NotImplementedError()
+
+ def create_networks(self, context, num_networks, network_size,
+ *args, **kwargs):
+ """Create networks based on parameters"""
+ raise NotImplementedError()
+
@property
def _bottom_reserved_ips(self): # pylint: disable-msg=R0201
"""Number of reserved ips at the bottom of the range"""
@@ -163,7 +201,7 @@ class NetworkManager(manager.Manager):
def _create_fixed_ips(self, context, network_id):
"""Create all fixed ips for network"""
network_ref = self.db.network_get(context, network_id)
- # NOTE(vish): should these be properties of the network as opposed
+ # NOTE(vish): Should these be properties of the network as opposed
# to properties of the manager class?
bottom_reserved = self._bottom_reserved_ips
top_reserved = self._top_reserved_ips
@@ -185,8 +223,13 @@ class FlatManager(NetworkManager):
def allocate_fixed_ip(self, context, instance_id, *args, **kwargs):
"""Gets a fixed ip from the pool"""
- network_ref = self.db.project_get_network(context, context.project.id)
- address = self.db.fixed_ip_associate_pool(context,
+ # TODO(vish): when this is called by compute, we can associate compute
+ # with a network, or a cluster of computes with a network
+ # and use that network here with a method like
+ # network_get_by_compute_host
+ network_ref = self.db.network_get_by_bridge(None,
+ FLAGS.flat_network_bridge)
+ address = self.db.fixed_ip_associate_pool(None,
network_ref['id'],
instance_id)
self.db.fixed_ip_update(context, address, {'allocated': True})
@@ -195,9 +238,9 @@ class FlatManager(NetworkManager):
def deallocate_fixed_ip(self, context, address, *args, **kwargs):
"""Returns a fixed ip to the pool"""
self.db.fixed_ip_update(context, address, {'allocated': False})
- self.db.fixed_ip_disassociate(context, address)
+ self.db.fixed_ip_disassociate(None, address)
- def setup_compute_network(self, context, project_id):
+ def setup_compute_network(self, context, instance_id):
"""Network is created manually"""
pass
@@ -205,24 +248,66 @@ class FlatManager(NetworkManager):
"""Currently no setup"""
pass
+ def create_networks(self, context, cidr, num_networks, network_size,
+ *args, **kwargs):
+ """Create networks based on parameters"""
+ fixed_net = IPy.IP(cidr)
+ for index in range(num_networks):
+ start = index * network_size
+ significant_bits = 32 - int(math.log(network_size, 2))
+ cidr = "%s/%s" % (fixed_net[start], significant_bits)
+ project_net = IPy.IP(cidr)
+ net = {}
+ net['cidr'] = cidr
+ net['netmask'] = str(project_net.netmask())
+ net['gateway'] = str(project_net[1])
+ net['broadcast'] = str(project_net.broadcast())
+ net['dhcp_start'] = str(project_net[2])
+ network_ref = self.db.network_create_safe(context, net)
+ if network_ref:
+ self._create_fixed_ips(context, network_ref['id'])
+
+ def get_network(self, context):
+ """Get the network for the current context"""
+ # NOTE(vish): To support mutilple network hosts, This could randomly
+ # select from multiple networks instead of just
+ # returning the one. It could also potentially be done
+ # in the scheduler.
+ return self.db.network_get_by_bridge(context,
+ FLAGS.flat_network_bridge)
+
def _on_set_network_host(self, context, network_id):
- """Called when this host becomes the host for a project"""
- # NOTE(vish): should there be two types of network objects
- # in the datastore?
+ """Called when this host becomes the host for a network"""
net = {}
net['injected'] = True
- net['netmask'] = FLAGS.flat_network_netmask
net['bridge'] = FLAGS.flat_network_bridge
- net['gateway'] = FLAGS.flat_network_gateway
- net['broadcast'] = FLAGS.flat_network_broadcast
net['dns'] = FLAGS.flat_network_dns
self.db.network_update(context, network_id, net)
- # NOTE(vish): Rignt now we are putting all of the fixed ips in
- # one large pool, but ultimately it may be better to
- # have each network manager have its own network that
- # it is responsible for and its own pool of ips.
- for address in FLAGS.flat_network_ips:
- self.db.fixed_ip_create(context, {'address': address})
+
+
+
+class FlatDHCPManager(NetworkManager):
+ """Flat networking with dhcp"""
+
+ def setup_fixed_ip(self, context, address):
+ """Setup dhcp for this network"""
+ network_ref = db.fixed_ip_get_by_address(context, address)
+ self.driver.update_dhcp(context, network_ref['id'])
+
+ def deallocate_fixed_ip(self, context, address, *args, **kwargs):
+ """Returns a fixed ip to the pool"""
+ self.db.fixed_ip_update(context, address, {'allocated': False})
+
+ def _on_set_network_host(self, context, network_id):
+ """Called when this host becomes the host for a project"""
+ super(FlatDHCPManager, self)._on_set_network_host(context, network_id)
+ network_ref = self.db.network_get(context, network_id)
+ self.db.network_update(context,
+ network_id,
+ {'dhcp_start': FLAGS.flat_network_dhcp_start})
+ self.driver.ensure_bridge(network_ref['bridge'],
+ FLAGS.bridge_dev,
+ network_ref)
class VlanManager(NetworkManager):
@@ -250,10 +335,13 @@ class VlanManager(NetworkManager):
def allocate_fixed_ip(self, context, instance_id, *args, **kwargs):
"""Gets a fixed ip from the pool"""
+ # TODO(vish): This should probably be getting project_id from
+ # the instance, but it is another trip to the db.
+ # Perhaps this method should take an instance_ref.
network_ref = self.db.project_get_network(context, context.project.id)
if kwargs.get('vpn', None):
address = network_ref['vpn_private_address']
- self.db.fixed_ip_associate(context, address, instance_id)
+ self.db.fixed_ip_associate(None, address, instance_id)
else:
address = self.db.fixed_ip_associate_pool(None,
network_ref['id'],
@@ -264,8 +352,6 @@ class VlanManager(NetworkManager):
def deallocate_fixed_ip(self, context, address, *args, **kwargs):
"""Returns a fixed ip to the pool"""
self.db.fixed_ip_update(context, address, {'allocated': False})
- fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
-
def setup_fixed_ip(self, context, address):
"""Sets forwarding rules and dhcp for fixed ip"""
@@ -277,80 +363,9 @@ class VlanManager(NetworkManager):
network_ref['vpn_private_address'])
self.driver.update_dhcp(context, network_ref['id'])
- def lease_fixed_ip(self, context, mac, address):
- """Called by dhcp-bridge when ip is leased"""
- logging.debug("Leasing IP %s", address)
- fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
- instance_ref = fixed_ip_ref['instance']
- if not instance_ref:
- raise exception.Error("IP %s leased that isn't associated" %
- address)
- if instance_ref['mac_address'] != mac:
- raise exception.Error("IP %s leased to bad mac %s vs %s" %
- (address, instance_ref['mac_address'], mac))
- self.db.fixed_ip_update(context,
- fixed_ip_ref['address'],
- {'leased': True})
- if not fixed_ip_ref['allocated']:
- logging.warn("IP %s leased that was already deallocated", address)
-
- def release_fixed_ip(self, context, mac, address):
- """Called by dhcp-bridge when ip is released"""
- logging.debug("Releasing IP %s", address)
- fixed_ip_ref = self.db.fixed_ip_get_by_address(context, address)
- instance_ref = fixed_ip_ref['instance']
- if not instance_ref:
- raise exception.Error("IP %s released that isn't associated" %
- address)
- if instance_ref['mac_address'] != mac:
- raise exception.Error("IP %s released from bad mac %s vs %s" %
- (address, instance_ref['mac_address'], mac))
- if not fixed_ip_ref['leased']:
- logging.warn("IP %s released that was not leased", address)
- self.db.fixed_ip_update(context,
- fixed_ip_ref['str_id'],
- {'leased': False})
- if not fixed_ip_ref['allocated']:
- self.db.fixed_ip_disassociate(context, address)
- # NOTE(vish): dhcp server isn't updated until next setup, this
- # means there will stale entries in the conf file
- # the code below will update the file if necessary
- if FLAGS.update_dhcp_on_disassociate:
- network_ref = self.db.fixed_ip_get_network(context, address)
- self.driver.update_dhcp(context, network_ref['id'])
-
- def allocate_network(self, context, project_id):
- """Set up the network"""
- self._ensure_indexes(context)
- network_ref = db.network_create(context, {'project_id': project_id})
- network_id = network_ref['id']
- private_net = IPy.IP(FLAGS.private_range)
- index = db.network_get_index(context, network_id)
- vlan = FLAGS.vlan_start + index
- start = index * FLAGS.network_size
- significant_bits = 32 - int(math.log(FLAGS.network_size, 2))
- cidr = "%s/%s" % (private_net[start], significant_bits)
- project_net = IPy.IP(cidr)
-
- net = {}
- net['cidr'] = cidr
- # NOTE(vish): we could turn these into properties
- net['netmask'] = str(project_net.netmask())
- net['gateway'] = str(project_net[1])
- net['broadcast'] = str(project_net.broadcast())
- net['vpn_private_address'] = str(project_net[2])
- net['dhcp_start'] = str(project_net[3])
- net['vlan'] = vlan
- net['bridge'] = 'br%s' % vlan
- net['vpn_public_address'] = FLAGS.vpn_ip
- net['vpn_public_port'] = FLAGS.vpn_start + index
- db.network_update(context, network_id, net)
- self._create_fixed_ips(context, network_id)
- return network_id
-
- def setup_compute_network(self, context, project_id):
+ def setup_compute_network(self, context, instance_id):
"""Sets up matching network for compute hosts"""
- network_ref = self.db.project_get_network(context, project_id)
+ network_ref = db.network_get_by_instance(context, instance_id)
self.driver.ensure_vlan_bridge(network_ref['vlan'],
network_ref['bridge'])
@@ -359,17 +374,42 @@ class VlanManager(NetworkManager):
# TODO(vish): Implement this
pass
- def _ensure_indexes(self, context):
- """Ensure the indexes for the network exist
-
- 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_safe(context, {'index': index})
+ def create_networks(self, context, cidr, num_networks, network_size,
+ vlan_start, vpn_start):
+ """Create networks based on parameters"""
+ fixed_net = IPy.IP(cidr)
+ for index in range(num_networks):
+ vlan = vlan_start + index
+ start = index * network_size
+ significant_bits = 32 - int(math.log(network_size, 2))
+ cidr = "%s/%s" % (fixed_net[start], significant_bits)
+ project_net = IPy.IP(cidr)
+ net = {}
+ net['cidr'] = cidr
+ net['netmask'] = str(project_net.netmask())
+ net['gateway'] = str(project_net[1])
+ net['broadcast'] = str(project_net.broadcast())
+ net['vpn_private_address'] = str(project_net[2])
+ net['dhcp_start'] = str(project_net[3])
+ net['vlan'] = vlan
+ net['bridge'] = 'br%s' % vlan
+ # NOTE(vish): This makes ports unique accross the cloud, a more
+ # robust solution would be to make them unique per ip
+ net['vpn_public_port'] = vpn_start + index
+ network_ref = self.db.network_create_safe(context, net)
+ if network_ref:
+ self._create_fixed_ips(context, network_ref['id'])
+
+ def get_network(self, context):
+ """Get the network for the current context"""
+ return self.db.project_get_network(None, context.project.id)
def _on_set_network_host(self, context, network_id):
- """Called when this host becomes the host for a project"""
+ """Called when this host becomes the host for a network"""
network_ref = self.db.network_get(context, network_id)
+ net = {}
+ net['vpn_public_address'] = FLAGS.vpn_ip
+ db.network_update(context, network_id, net)
self.driver.ensure_vlan_bridge(network_ref['vlan'],
network_ref['bridge'],
network_ref)
diff --git a/nova/test.py b/nova/test.py
index 08e1dea2d..f6485377d 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -24,6 +24,7 @@ and some black magic for inline callbacks.
import sys
import time
+import datetime
import mox
import stubout
@@ -35,6 +36,7 @@ from nova import db
from nova import fakerabbit
from nova import flags
from nova import rpc
+from nova.network import manager as network_manager
FLAGS = flags.FLAGS
@@ -58,6 +60,16 @@ class TrialTestCase(unittest.TestCase):
def setUp(self): # pylint: disable-msg=C0103
"""Run before each test method to initialize test environment"""
super(TrialTestCase, self).setUp()
+ # NOTE(vish): We need a better method for creating fixtures for tests
+ # now that we have some required db setup for the system
+ # to work properly.
+ self.start = datetime.datetime.utcnow()
+ if db.network_count(None) != 5:
+ network_manager.VlanManager().create_networks(None,
+ FLAGS.fixed_range,
+ 5, 16,
+ FLAGS.vlan_start,
+ FLAGS.vpn_start)
# emulate some of the mox stuff, we can't use the metaclass
# because it screws with our generators
@@ -74,7 +86,9 @@ class TrialTestCase(unittest.TestCase):
self.stubs.UnsetAll()
self.stubs.SmartUnsetAll()
self.mox.VerifyAll()
-
+ # NOTE(vish): Clean up any ips associated during the test.
+ db.fixed_ip_disassociate_all_by_timeout(None, FLAGS.host, self.start)
+ db.network_disassociate_all(None)
rpc.Consumer.attach_to_twisted = self.originalAttach
for x in self.injected:
try:
@@ -140,7 +154,7 @@ class TrialTestCase(unittest.TestCase):
class BaseTestCase(TrialTestCase):
# TODO(jaypipes): Can this be moved into the TrialTestCase class?
"""Base test case class for all unit tests.
-
+
DEPRECATED: This is being removed once Tornado is gone, use TrialTestCase.
"""
def setUp(self): # pylint: disable-msg=C0103
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 34bc1f2a9..71da2fd21 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -161,6 +161,10 @@ def stub_out_glance(stubs):
stubs.Set(nova.image.service.GlanceImageService, 'delete_all',
fake_parallax_client.fake_delete_all)
+class FakeToken(object):
+ def __init__(self, **kwargs):
+ for k,v in kwargs.iteritems():
+ setattr(self, k, v)
class FakeAuthDatabase(object):
data = {}
@@ -171,12 +175,13 @@ class FakeAuthDatabase(object):
@staticmethod
def auth_create_token(context, token):
- token['created_at'] = datetime.datetime.now()
- FakeAuthDatabase.data[token['token_hash']] = token
+ fake_token = FakeToken(created_at=datetime.datetime.now(), **token)
+ FakeAuthDatabase.data[fake_token.token_hash] = fake_token
+ return fake_token
@staticmethod
def auth_destroy_token(context, token):
- if FakeAuthDatabase.data.has_key(token['token_hash']):
+ if token.token_hash in FakeAuthDatabase.data:
del FakeAuthDatabase.data['token_hash']
@@ -188,7 +193,7 @@ class FakeAuthManager(object):
def get_user(self, uid):
for k, v in FakeAuthManager.auth_data.iteritems():
- if v['uid'] == uid:
+ if v.id == uid:
return v
return None
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index d2ba80243..bbfb0fcea 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -7,6 +7,7 @@ import webob.dec
import nova.api
import nova.api.openstack.auth
+import nova.auth.manager
from nova import auth
from nova.tests.api.openstack import fakes
@@ -26,7 +27,7 @@ class Test(unittest.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', { 'uid': 1, 'name':'herp' } )
+ f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -40,7 +41,7 @@ class Test(unittest.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', { 'uid': 1, 'name':'herp' } )
+ f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -71,8 +72,9 @@ class Test(unittest.TestCase):
self.destroy_called = True
def bad_token(meh, context, token_hash):
- return { 'token_hash':token_hash,
- 'created_at':datetime.datetime(1990, 1, 1) }
+ return fakes.FakeToken(
+ token_hash=token_hash,
+ created_at=datetime.datetime(1990, 1, 1))
self.stubs.Set(fakes.FakeAuthDatabase, 'auth_destroy_token',
destroy_token_mock)
diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py
index 8e5881edb..ff466135d 100644
--- a/nova/tests/cloud_unittest.py
+++ b/nova/tests/cloud_unittest.py
@@ -64,18 +64,17 @@ class CloudTestCase(test.TrialTestCase):
self.cloud = cloud.CloudController()
# set up a service
- self.compute = utils.import_class(FLAGS.compute_manager)()
+ self.compute = utils.import_object(FLAGS.compute_manager)
self.compute_consumer = rpc.AdapterConsumer(connection=self.conn,
topic=FLAGS.compute_topic,
proxy=self.compute)
self.compute_consumer.attach_to_eventlet()
- self.network = utils.import_class(FLAGS.network_manager)()
+ self.network = utils.import_object(FLAGS.network_manager)
self.network_consumer = rpc.AdapterConsumer(connection=self.conn,
topic=FLAGS.network_topic,
proxy=self.network)
self.network_consumer.attach_to_eventlet()
-
self.manager = manager.AuthManager()
self.user = self.manager.create_user('admin', 'admin', 'admin', True)
self.project = self.manager.create_project('proj', 'admin', 'proj')
diff --git a/nova/tests/compute_unittest.py b/nova/tests/compute_unittest.py
index 1e2bb113b..5a7f170f3 100644
--- a/nova/tests/compute_unittest.py
+++ b/nova/tests/compute_unittest.py
@@ -40,7 +40,8 @@ class ComputeTestCase(test.TrialTestCase):
def setUp(self): # pylint: disable-msg=C0103
logging.getLogger().setLevel(logging.DEBUG)
super(ComputeTestCase, self).setUp()
- self.flags(connection_type='fake')
+ self.flags(connection_type='fake',
+ network_manager='nova.network.manager.FlatManager')
self.compute = utils.import_object(FLAGS.compute_manager)
self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake')
diff --git a/nova/tests/network_unittest.py b/nova/tests/network_unittest.py
index 59b0a36e4..3afb4d19e 100644
--- a/nova/tests/network_unittest.py
+++ b/nova/tests/network_unittest.py
@@ -52,13 +52,14 @@ class NetworkTestCase(test.TrialTestCase):
self.context = context.APIRequestContext(project=None, user=self.user)
for i in range(5):
name = 'project%s' % i
- self.projects.append(self.manager.create_project(name,
- 'netuser',
- name))
+ project = self.manager.create_project(name, 'netuser', name)
+ self.projects.append(project)
# create the necessary network data for the project
- user_context = context.get_admin_context(user=self.user)
-
- self.network.set_network_host(user_context, self.projects[i].id)
+ user_context = context.APIRequestContext(project=self.projects[i],
+ user=self.user)
+ network_ref = self.network.get_network(user_context)
+ self.network.set_network_host(context.get_admin_context(),
+ network_ref['id'])
instance_ref = self._create_instance(0)
self.instance_id = instance_ref['id']
instance_ref = self._create_instance(1)
@@ -99,7 +100,7 @@ class NetworkTestCase(test.TrialTestCase):
"""Makes sure that we can allocaate a public ip"""
# TODO(vish): better way of adding floating ips
self.context.project = self.projects[0]
- pubnet = IPy.IP(flags.FLAGS.public_range)
+ pubnet = IPy.IP(flags.FLAGS.floating_range)
address = str(pubnet[0])
try:
db.floating_ip_get_by_address(None, address)
@@ -109,6 +110,7 @@ class NetworkTestCase(test.TrialTestCase):
float_addr = self.network.allocate_floating_ip(self.context,
self.projects[0].id)
fix_addr = self._create_address(0)
+ lease_ip(fix_addr)
self.assertEqual(float_addr, str(pubnet[0]))
self.network.associate_floating_ip(self.context, float_addr, fix_addr)
address = db.instance_get_floating_address(None, self.instance_id)
@@ -118,6 +120,7 @@ class NetworkTestCase(test.TrialTestCase):
self.assertEqual(address, None)
self.network.deallocate_floating_ip(self.context, float_addr)
self.network.deallocate_fixed_ip(self.context, fix_addr)
+ release_ip(fix_addr)
def test_allocate_deallocate_fixed_ip(self):
"""Makes sure that we can allocate and deallocate a fixed ip"""
@@ -190,8 +193,10 @@ class NetworkTestCase(test.TrialTestCase):
release_ip(address3)
for instance_id in instance_ids:
db.instance_destroy(None, instance_id)
- release_ip(first)
+ self.context.project = self.projects[0]
+ self.network.deallocate_fixed_ip(self.context, first)
self._deallocate_address(0, first)
+ release_ip(first)
def test_vpn_ip_and_port_looks_valid(self):
"""Ensure the vpn ip and port are reasonable"""
@@ -207,10 +212,13 @@ class NetworkTestCase(test.TrialTestCase):
for i in range(networks_left):
project = self.manager.create_project('many%s' % i, self.user)
projects.append(project)
+ db.project_get_network(None, project.id)
+ project = self.manager.create_project('last', self.user)
+ projects.append(project)
self.assertRaises(db.NoMoreNetworks,
- self.manager.create_project,
- 'boom',
- self.user)
+ db.project_get_network,
+ None,
+ project.id)
for project in projects:
self.manager.delete_project(project)
@@ -223,7 +231,9 @@ class NetworkTestCase(test.TrialTestCase):
address2 = self._create_address(0)
self.assertEqual(address, address2)
+ lease_ip(address)
self.network.deallocate_fixed_ip(self.context, address2)
+ release_ip(address)
def test_available_ips(self):
"""Make sure the number of available ips for the network is correct
diff --git a/nova/tests/scheduler_unittest.py b/nova/tests/scheduler_unittest.py
index 53a8be144..80100fc2f 100644
--- a/nova/tests/scheduler_unittest.py
+++ b/nova/tests/scheduler_unittest.py
@@ -75,6 +75,7 @@ class SimpleDriverTestCase(test.TrialTestCase):
self.flags(connection_type='fake',
max_cores=4,
max_gigabytes=4,
+ network_manager='nova.network.manager.FlatManager',
volume_driver='nova.volume.driver.FakeAOEDriver',
scheduler_driver='nova.scheduler.simple.SimpleScheduler')
self.scheduler = manager.SchedulerManager()
diff --git a/nova/tests/virt_unittest.py b/nova/tests/virt_unittest.py
index 684347473..edcdba425 100644
--- a/nova/tests/virt_unittest.py
+++ b/nova/tests/virt_unittest.py
@@ -20,21 +20,22 @@ from xml.dom.minidom import parseString as xml_to_dom
from nova import db
from nova import flags
from nova import test
+from nova import utils
from nova.api import context
from nova.api.ec2 import cloud
from nova.auth import manager
-
-# Needed to get FLAGS.instances_path defined:
-from nova.compute import manager as compute_manager
from nova.virt import libvirt_conn
FLAGS = flags.FLAGS
+flags.DECLARE('instances_path', 'nova.compute.manager')
class LibvirtConnTestCase(test.TrialTestCase):
def setUp(self):
+ super(LibvirtConnTestCase, self).setUp()
self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake', admin=True)
self.project = self.manager.create_project('fake', 'fake', 'fake')
+ self.network = utils.import_object(FLAGS.network_manager)
FLAGS.instances_path = ''
def test_get_uri_and_template(self):
@@ -51,11 +52,15 @@ class LibvirtConnTestCase(test.TrialTestCase):
'instance_type' : 'm1.small'}
instance_ref = db.instance_create(None, instance)
- network_ref = db.project_get_network(None, self.project.id)
+ user_context = context.APIRequestContext(project=self.project,
+ user=self.user)
+ network_ref = self.network.get_network(user_context)
+ self.network.set_network_host(context.get_admin_context(),
+ network_ref['id'])
fixed_ip = { 'address' : ip,
'network_id' : network_ref['id'] }
-
+
fixed_ip_ref = db.fixed_ip_create(None, fixed_ip)
db.fixed_ip_update(None, ip, { 'allocated' : True,
'instance_id' : instance_ref['id'] })
@@ -113,6 +118,7 @@ class LibvirtConnTestCase(test.TrialTestCase):
def tearDown(self):
+ super(LibvirtConnTestCase, self).tearDown()
self.manager.delete_project(self.project)
self.manager.delete_user(self.user)
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 4ae6afcc4..dc6112f20 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -223,6 +223,8 @@ class FakeConnection(object):
"""
return [0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L]
+ def get_console_output(self, instance):
+ return 'FAKE CONSOLE OUTPUT'
class FakeInstance(object):
def __init__(self):
diff --git a/nova/virt/libvirt.xen.xml.template b/nova/virt/libvirt.xen.xml.template
new file mode 100644
index 000000000..9677902c6
--- /dev/null
+++ b/nova/virt/libvirt.xen.xml.template
@@ -0,0 +1,30 @@
+<domain type='%(type)s'>
+ <name>%(name)s</name>
+ <os>
+ <type>linux</type>
+ <kernel>%(basepath)s/kernel</kernel>
+ <initrd>%(basepath)s/ramdisk</initrd>
+ <root>/dev/xvda1</root>
+ <cmdline>ro</cmdline>
+ </os>
+ <features>
+ <acpi/>
+ </features>
+ <memory>%(memory_kb)s</memory>
+ <vcpu>%(vcpus)s</vcpu>
+ <devices>
+ <disk type='file'>
+ <source file='%(basepath)s/disk'/>
+ <target dev='sda' />
+ </disk>
+ <interface type='bridge'>
+ <source bridge='%(bridge_name)s'/>
+ <mac address='%(mac_address)s'/>
+ </interface>
+ <console type="file">
+ <source path='%(basepath)s/console.log'/>
+ <target port='1'/>
+ </console>
+ </devices>
+</domain>
+
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 319f7d2af..ce97ef1eb 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -50,6 +50,9 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('libvirt_xml_template',
utils.abspath('virt/libvirt.qemu.xml.template'),
'Libvirt XML Template for QEmu/KVM')
+flags.DEFINE_string('libvirt_xen_xml_template',
+ utils.abspath('virt/libvirt.xen.xml.template'),
+ 'Libvirt XML Template for Xen')
flags.DEFINE_string('libvirt_uml_xml_template',
utils.abspath('virt/libvirt.uml.xml.template'),
'Libvirt XML Template for user-mode-linux')
@@ -58,7 +61,7 @@ flags.DEFINE_string('injected_network_template',
'Template file for injected network')
flags.DEFINE_string('libvirt_type',
'kvm',
- 'Libvirt domain type (valid options are: kvm, qemu, uml)')
+ 'Libvirt domain type (valid options are: kvm, qemu, uml, xen)')
flags.DEFINE_string('libvirt_uri',
'',
'Override the default libvirt URI (which is dependent'
@@ -110,6 +113,9 @@ class LibvirtConnection(object):
if FLAGS.libvirt_type == 'uml':
uri = FLAGS.libvirt_uri or 'uml:///system'
template_file = FLAGS.libvirt_uml_xml_template
+ elif FLAGS.libvirt_type == 'xen':
+ uri = FLAGS.libvirt_uri or 'xen:///'
+ template_file = FLAGS.libvirt_xen_xml_template
else:
uri = FLAGS.libvirt_uri or 'qemu:///system'
template_file = FLAGS.libvirt_xml_template
@@ -249,6 +255,46 @@ class LibvirtConnection(object):
timer.start(interval=0.5, now=True)
yield local_d
+ def _flush_xen_console(self, virsh_output):
+ logging.info('virsh said: %r' % (virsh_output,))
+ virsh_output = virsh_output[0].strip()
+
+ if virsh_output.startswith('/dev/'):
+ logging.info('cool, it\'s a device')
+ d = process.simple_execute("sudo dd if=%s iflag=nonblock" % virsh_output, check_exit_code=False)
+ d.addCallback(lambda r:r[0])
+ return d
+ else:
+ return ''
+
+ def _append_to_file(self, data, fpath):
+ logging.info('data: %r, fpath: %r' % (data, fpath))
+ fp = open(fpath, 'a+')
+ fp.write(data)
+ return fpath
+
+ def _dump_file(self, fpath):
+ fp = open(fpath, 'r+')
+ contents = fp.read()
+ logging.info('Contents: %r' % (contents,))
+ return contents
+
+ @exception.wrap_exception
+ def get_console_output(self, instance):
+ console_log = os.path.join(FLAGS.instances_path, instance['internal_id'], 'console.log')
+ logging.info('console_log: %s' % console_log)
+ logging.info('FLAGS.libvirt_type: %s' % FLAGS.libvirt_type)
+ if FLAGS.libvirt_type == 'xen':
+ # Xen is spethial
+ d = process.simple_execute("virsh ttyconsole %s" % instance['name'])
+ d.addCallback(self._flush_xen_console)
+ d.addCallback(self._append_to_file, console_log)
+ else:
+ d = defer.succeed(console_log)
+ d.addCallback(self._dump_file)
+ return d
+
+
@defer.inlineCallbacks
def _create_image(self, inst, libvirt_xml):
# syntactic nicety
@@ -287,7 +333,7 @@ class LibvirtConnection(object):
key = str(inst['key_data'])
net = None
- network_ref = db.project_get_network(None, project.id)
+ network_ref = db.network_get_by_instance(None, inst['id'])
if network_ref['injected']:
address = db.instance_get_fixed_address(None, inst['id'])
with open(FLAGS.injected_network_template) as f:
@@ -320,7 +366,8 @@ class LibvirtConnection(object):
def to_xml(self, instance):
# TODO(termie): cache?
logging.debug('instance %s: starting toXML method', instance['name'])
- network = db.project_get_network(None, instance['project_id'])
+ network = db.project_get_network(None,
+ instance['project_id'])
# FIXME(vish): stick this in db
instance_type = instance_types.INSTANCE_TYPES[instance['instance_type']]
ip_address = db.instance_get_fixed_address({}, instance['id'])
diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py
index 118e0b687..04e830b64 100644
--- a/nova/virt/xenapi.py
+++ b/nova/virt/xenapi.py
@@ -294,6 +294,9 @@ class XenAPIConnection(object):
'num_cpu': rec['VCPUs_max'],
'cpu_time': 0}
+ def get_console_output(self, instance):
+ return 'FAKE CONSOLE OUTPUT'
+
@utils.deferredToThread
def _lookup(self, i):
return self._lookup_blocking(i)