summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Windisch <eric@cloudscaling.com>2011-03-09 17:05:19 -0500
committerEric Windisch <eric@cloudscaling.com>2011-03-09 17:05:19 -0500
commitd9c9b084539ca3bdcb58cd037ab6c297bfc28808 (patch)
tree315be5e6fe998f85bac24f47b4bb27badde9504a
parente8554da80ac916f168461cb48078488700081c02 (diff)
parentb5d7065512a155d5ad23b4f240645f7bee03f7cb (diff)
downloadnova-d9c9b084539ca3bdcb58cd037ab6c297bfc28808.tar.gz
nova-d9c9b084539ca3bdcb58cd037ab6c297bfc28808.tar.xz
nova-d9c9b084539ca3bdcb58cd037ab6c297bfc28808.zip
Merge with main
-rwxr-xr-xbin/nova-manage9
-rw-r--r--nova/api/openstack/common.py11
-rw-r--r--nova/api/openstack/servers.py11
-rw-r--r--nova/compute/api.py5
-rw-r--r--nova/db/api.py12
-rw-r--r--nova/db/sqlalchemy/api.py21
-rw-r--r--nova/flags.py2
-rw-r--r--nova/network/manager.py10
-rw-r--r--nova/tests/api/openstack/test_common.py20
-rw-r--r--nova/tests/api/openstack/test_servers.py36
-rw-r--r--nova/tests/test_misc.py48
-rw-r--r--nova/utils.py20
-rwxr-xr-xtools/euca-get-ajax-console4
13 files changed, 179 insertions, 30 deletions
diff --git a/bin/nova-manage b/bin/nova-manage
index 9bf3a1bb3..7dfc3c045 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -545,6 +545,15 @@ class NetworkCommands(object):
network.dhcp_start,
network.dns)
+ def delete(self, fixed_range):
+ """Deletes a network"""
+ network = db.network_get_by_cidr(context.get_admin_context(), \
+ fixed_range)
+ if network.project_id is not None:
+ raise ValueError(_('Network must be disassociated from project %s'
+ ' before delete' % network.project_id))
+ db.network_delete_safe(context.get_admin_context(), network.id)
+
class ServiceCommands(object):
"""Enable and disable running services"""
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 9f85c5c8a..f7a9cc3f0 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -36,15 +36,18 @@ def limited(items, request, max_limit=1000):
try:
offset = int(request.GET.get('offset', 0))
except ValueError:
- offset = 0
+ raise webob.exc.HTTPBadRequest(_('offset param must be an integer'))
try:
limit = int(request.GET.get('limit', max_limit))
except ValueError:
- limit = max_limit
+ raise webob.exc.HTTPBadRequest(_('limit param must be an integer'))
- if offset < 0 or limit < 0:
- raise webob.exc.HTTPBadRequest()
+ if limit < 0:
+ raise webob.exc.HTTPBadRequest(_('limit param must be positive'))
+
+ if offset < 0:
+ raise webob.exc.HTTPBadRequest(_('offset param must be positive'))
limit = min(max_limit, limit or max_limit)
range_end = offset + limit
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 85999764f..41166f810 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -98,7 +98,7 @@ class Controller(wsgi.Controller):
'application/xml': {
"attributes": {
"server": ["id", "imageId", "name", "flavorId", "hostId",
- "status", "progress"]}}}
+ "status", "progress", "adminPass"]}}}
def __init__(self):
self.compute_api = compute.API()
@@ -178,7 +178,14 @@ class Controller(wsgi.Controller):
key_data=key_pair['public_key'],
metadata=metadata,
onset_files=env.get('onset_files', []))
- return _translate_keys(instances[0])
+
+ server = _translate_keys(instances[0])
+ password = "%s%s" % (server['server']['name'][:4],
+ utils.generate_password(12))
+ server['server']['adminPass'] = password
+ self.compute_api.set_admin_password(context, server['server']['id'],
+ password)
+ return server
def update(self, req, id):
""" Updates the server name or password """
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 33d25fc4b..a0bb2cf04 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -498,9 +498,10 @@ class API(base.Base):
"""Unrescue the given instance."""
self._cast_compute_message('unrescue_instance', context, instance_id)
- def set_admin_password(self, context, instance_id):
+ def set_admin_password(self, context, instance_id, password=None):
"""Set the root/admin password for the given instance."""
- self._cast_compute_message('set_admin_password', context, instance_id)
+ self._cast_compute_message('set_admin_password', context, instance_id,
+ password)
def inject_file(self, context, instance_id):
"""Write a file to the given instance."""
diff --git a/nova/db/api.py b/nova/db/api.py
index 2ecfc0211..aa86f0af1 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -517,6 +517,13 @@ def network_create_safe(context, values):
return IMPL.network_create_safe(context, values)
+def network_delete_safe(context, network_id):
+ """Delete network with key network_id.
+ This method assumes that the network is not associated with any project
+ """
+ return IMPL.network_delete_safe(context, network_id)
+
+
def network_create_fixed_ips(context, network_id, num_vpn_clients):
"""Create the ips for the network, reserving sepecified ips."""
return IMPL.network_create_fixed_ips(context, network_id, num_vpn_clients)
@@ -553,6 +560,11 @@ def network_get_by_bridge(context, bridge):
return IMPL.network_get_by_bridge(context, bridge)
+def network_get_by_cidr(context, cidr):
+ """Get a network by cidr or raise if it does not exist"""
+ return IMPL.network_get_by_cidr(context, cidr)
+
+
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)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 5e498fc6f..3e94082df 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1055,6 +1055,15 @@ def network_create_safe(context, values):
@require_admin_context
+def network_delete_safe(context, network_id):
+ session = get_session()
+ with session.begin():
+ network_ref = network_get(context, network_id=network_id, \
+ session=session)
+ session.delete(network_ref)
+
+
+@require_admin_context
def network_disassociate(context, network_id):
network_update(context, network_id, {'project_id': None,
'host': None})
@@ -1128,6 +1137,18 @@ def network_get_by_bridge(context, bridge):
@require_admin_context
+def network_get_by_cidr(context, cidr):
+ session = get_session()
+ result = session.query(models.Network).\
+ filter_by(cidr=cidr).first()
+
+ if not result:
+ raise exception.NotFound(_('Network with cidr %s does not exist') %
+ cidr)
+ return result
+
+
+@require_admin_context
def network_get_by_instance(_context, instance_id):
session = get_session()
rv = session.query(models.Network).\
diff --git a/nova/flags.py b/nova/flags.py
index 8cf199b2f..213d4d4e1 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -321,6 +321,8 @@ DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')
DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
"Top-level directory for maintaining nova's state")
+DEFINE_string('lock_path', os.path.join(os.path.dirname(__file__), '../'),
+ "Directory for lock files")
DEFINE_string('logdir', None, 'output to a per-service log file in named '
'directory')
diff --git a/nova/network/manager.py b/nova/network/manager.py
index b36dd59cf..3dfc48934 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -563,6 +563,16 @@ class VlanManager(NetworkManager):
# 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 = None
+ try:
+ network_ref = db.network_get_by_cidr(context, cidr)
+ except exception.NotFound:
+ pass
+
+ if network_ref is not None:
+ raise ValueError(_('Network with cidr %s already exists' %
+ cidr))
+
network_ref = self.db.network_create_safe(context, net)
if network_ref:
self._create_fixed_ips(context, network_ref['id'])
diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py
index 92023362c..8f57c5b67 100644
--- a/nova/tests/api/openstack/test_common.py
+++ b/nova/tests/api/openstack/test_common.py
@@ -79,20 +79,14 @@ class LimiterTest(test.TestCase):
Test offset key works with a blank offset.
"""
req = Request.blank('/?offset=')
- self.assertEqual(limited(self.tiny, req), self.tiny)
- self.assertEqual(limited(self.small, req), self.small)
- self.assertEqual(limited(self.medium, req), self.medium)
- self.assertEqual(limited(self.large, req), self.large[:1000])
+ self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req)
def test_limiter_offset_bad(self):
"""
Test offset key works with a BAD offset.
"""
req = Request.blank(u'/?offset=\u0020aa')
- self.assertEqual(limited(self.tiny, req), self.tiny)
- self.assertEqual(limited(self.small, req), self.small)
- self.assertEqual(limited(self.medium, req), self.medium)
- self.assertEqual(limited(self.large, req), self.large[:1000])
+ self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req)
def test_limiter_nothing(self):
"""
@@ -166,18 +160,12 @@ class LimiterTest(test.TestCase):
"""
Test a negative limit.
"""
- def _limit_large():
- limited(self.large, req, max_limit=2000)
-
req = Request.blank('/?limit=-3000')
- self.assertRaises(webob.exc.HTTPBadRequest, _limit_large)
+ self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req)
def test_limiter_negative_offset(self):
"""
Test a negative offset.
"""
- def _limit_large():
- limited(self.large, req, max_limit=2000)
-
req = Request.blank('/?offset=-30')
- self.assertRaises(webob.exc.HTTPBadRequest, _limit_large)
+ self.assertRaises(webob.exc.HTTPBadRequest, limited, self.tiny, req)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index c9566c7e6..3739329f2 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -188,9 +188,37 @@ class ServersTest(test.TestCase):
self.assertEqual(s.get('imageId', None), None)
i += 1
+ def test_get_servers_with_limit(self):
+ req = webob.Request.blank('/v1.0/servers?limit=3')
+ res = req.get_response(fakes.wsgi_app())
+ servers = json.loads(res.body)['servers']
+ self.assertEqual([s['id'] for s in servers], [0, 1, 2])
+
+ req = webob.Request.blank('/v1.0/servers?limit=aaa')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+ self.assertTrue('limit' in res.body)
+
+ def test_get_servers_with_offset(self):
+ req = webob.Request.blank('/v1.0/servers?offset=2')
+ res = req.get_response(fakes.wsgi_app())
+ servers = json.loads(res.body)['servers']
+ self.assertEqual([s['id'] for s in servers], [2, 3, 4])
+
+ req = webob.Request.blank('/v1.0/servers?offset=aaa')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 400)
+ self.assertTrue('offset' in res.body)
+
+ def test_get_servers_with_limit_and_offset(self):
+ req = webob.Request.blank('/v1.0/servers?limit=2&offset=1')
+ res = req.get_response(fakes.wsgi_app())
+ servers = json.loads(res.body)['servers']
+ self.assertEqual([s['id'] for s in servers], [1, 2])
+
def test_create_instance(self):
def instance_create(context, inst):
- return {'id': '1', 'display_name': ''}
+ return {'id': '1', 'display_name': 'server_test'}
def server_update(context, id, params):
return instance_create(context, id)
@@ -234,6 +262,12 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
+ server = json.loads(res.body)['server']
+ self.assertEqual('serv', server['adminPass'][:4])
+ self.assertEqual(16, len(server['adminPass']))
+ self.assertEqual('server_test', server['name'])
+ self.assertEqual('1', server['id'])
+
self.assertEqual(res.status_int, 200)
def test_update_no_body(self):
diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py
index e6da6112a..a658e4978 100644
--- a/nova/tests/test_misc.py
+++ b/nova/tests/test_misc.py
@@ -14,10 +14,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import errno
import os
+import select
from nova import test
-from nova.utils import parse_mailmap, str_dict_replace
+from nova.utils import parse_mailmap, str_dict_replace, synchronized
class ProjectTestCase(test.TestCase):
@@ -55,3 +57,47 @@ class ProjectTestCase(test.TestCase):
'%r not listed in Authors' % missing)
finally:
tree.unlock()
+
+
+class LockTestCase(test.TestCase):
+ def test_synchronized_wrapped_function_metadata(self):
+ @synchronized('whatever')
+ def foo():
+ """Bar"""
+ pass
+ self.assertEquals(foo.__doc__, 'Bar', "Wrapped function's docstring "
+ "got lost")
+ self.assertEquals(foo.__name__, 'foo', "Wrapped function's name "
+ "got mangled")
+
+ def test_synchronized(self):
+ rpipe1, wpipe1 = os.pipe()
+ rpipe2, wpipe2 = os.pipe()
+
+ @synchronized('testlock')
+ def f(rpipe, wpipe):
+ try:
+ os.write(wpipe, "foo")
+ except OSError, e:
+ self.assertEquals(e.errno, errno.EPIPE)
+ return
+
+ rfds, _, __ = select.select([rpipe], [], [], 1)
+ self.assertEquals(len(rfds), 0, "The other process, which was"
+ " supposed to be locked, "
+ "wrote on its end of the "
+ "pipe")
+ os.close(rpipe)
+
+ pid = os.fork()
+ if pid > 0:
+ os.close(wpipe1)
+ os.close(rpipe2)
+
+ f(rpipe1, wpipe2)
+ else:
+ os.close(rpipe1)
+ os.close(wpipe2)
+
+ f(rpipe2, wpipe1)
+ os._exit(0)
diff --git a/nova/utils.py b/nova/utils.py
index 3a4ec3c6a..0466fecf4 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -23,10 +23,14 @@ System-level utilities and helper functions.
import base64
import datetime
+import functools
import inspect
import json
+import lockfile
+import netaddr
import os
import random
+import re
import socket
import string
import struct
@@ -34,8 +38,6 @@ import sys
import time
import types
from xml.sax import saxutils
-import re
-import netaddr
from eventlet import event
from eventlet import greenthread
@@ -43,11 +45,13 @@ from eventlet.green import subprocess
None
from nova import exception
from nova.exception import ProcessExecutionError
+from nova import flags
from nova import log as logging
LOG = logging.getLogger("nova.utils")
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
+FLAGS = flags.FLAGS
def import_class(import_str):
@@ -500,6 +504,18 @@ def loads(s):
return json.loads(s)
+def synchronized(name):
+ def wrap(f):
+ @functools.wraps(f)
+ def inner(*args, **kwargs):
+ lock = lockfile.FileLock(os.path.join(FLAGS.lock_path,
+ 'nova-%s.lock' % name))
+ with lock:
+ return f(*args, **kwargs)
+ return inner
+ return wrap
+
+
def ensure_b64_encoding(val):
"""Safety method to ensure that values expected to be base64-encoded
actually are. If they are, the value is returned unchanged. Otherwise,
diff --git a/tools/euca-get-ajax-console b/tools/euca-get-ajax-console
index 37060e74f..e407dd566 100755
--- a/tools/euca-get-ajax-console
+++ b/tools/euca-get-ajax-console
@@ -35,7 +35,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
import boto
import nova
from boto.ec2.connection import EC2Connection
-from euca2ools import Euca2ool, InstanceValidationError, Util, ConnectionFailed
+from euca2ools import Euca2ool, InstanceValidationError, Util
usage_string = """
Retrieves a url to an ajax console terminal
@@ -147,7 +147,7 @@ def main():
try:
euca_conn = euca.make_connection()
- except ConnectionFailed, e:
+ except Exception, e:
print e.message
sys.exit(1)
try: