Merging trunk

This commit is contained in:
Rick Harris 2011-05-20 10:31:23 -05:00
commit 821ee03ced
13 changed files with 259 additions and 262 deletions

View File

@ -27,6 +27,8 @@ import datetime
import IPy import IPy
import os import os
import urllib import urllib
import tempfile
import shutil
from nova import compute from nova import compute
from nova import context from nova import context
@ -316,6 +318,27 @@ class CloudController(object):
'keyMaterial': data['private_key']} 'keyMaterial': data['private_key']}
# TODO(vish): when context is no longer an object, pass it here # TODO(vish): when context is no longer an object, pass it here
def import_public_key(self, context, key_name, public_key,
fingerprint=None):
LOG.audit(_("Import key %s"), key_name, context=context)
key = {}
key['user_id'] = context.user_id
key['name'] = key_name
key['public_key'] = public_key
if fingerprint is None:
tmpdir = tempfile.mkdtemp()
pubfile = os.path.join(tmpdir, 'temp.pub')
fh = open(pubfile, 'w')
fh.write(public_key)
fh.close()
(out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f',
'%s' % (pubfile))
fingerprint = out.split(' ')[1]
shutil.rmtree(tmpdir)
key['fingerprint'] = fingerprint
db.key_pair_create(context, key)
return True
def delete_key_pair(self, context, key_name, **kwargs): def delete_key_pair(self, context, key_name, **kwargs):
LOG.audit(_("Delete key pair %s"), key_name, context=context) LOG.audit(_("Delete key pair %s"), key_name, context=context)
try: try:

View File

@ -105,15 +105,14 @@ class ExtensionDescriptor(object):
actions = [] actions = []
return actions return actions
def get_response_extensions(self): def get_request_extensions(self):
"""List of extensions.ResponseExtension extension objects. """List of extensions.RequestException extension objects.
Response extensions are used to insert information into existing Request extensions are used to handle custom request data.
response data.
""" """
response_exts = [] request_exts = []
return response_exts return request_exts
class ActionExtensionController(common.OpenstackController): class ActionExtensionController(common.OpenstackController):
@ -137,7 +136,7 @@ class ActionExtensionController(common.OpenstackController):
return res return res
class ResponseExtensionController(common.OpenstackController): class RequestExtensionController(common.OpenstackController):
def __init__(self, application): def __init__(self, application):
self.application = application self.application = application
@ -148,20 +147,9 @@ class ResponseExtensionController(common.OpenstackController):
def process(self, req, *args, **kwargs): def process(self, req, *args, **kwargs):
res = req.get_response(self.application) res = req.get_response(self.application)
content_type = req.best_match_content_type() # currently request handlers are un-ordered
# currently response handlers are un-ordered
for handler in self.handlers: for handler in self.handlers:
res = handler(res) res = handler(req, res)
try:
body = res.body
headers = res.headers
except AttributeError:
default_xmlns = None
body = self._serialize(res, content_type, default_xmlns)
headers = {"Content-Type": content_type}
res = webob.Response()
res.body = body
res.headers = headers
return res return res
@ -226,24 +214,24 @@ class ExtensionMiddleware(wsgi.Middleware):
return action_controllers return action_controllers
def _response_ext_controllers(self, application, ext_mgr, mapper): def _request_ext_controllers(self, application, ext_mgr, mapper):
"""Returns a dict of ResponseExtensionController-s by collection.""" """Returns a dict of RequestExtensionController-s by collection."""
response_ext_controllers = {} request_ext_controllers = {}
for resp_ext in ext_mgr.get_response_extensions(): for req_ext in ext_mgr.get_request_extensions():
if not resp_ext.key in response_ext_controllers.keys(): if not req_ext.key in request_ext_controllers.keys():
controller = ResponseExtensionController(application) controller = RequestExtensionController(application)
mapper.connect(resp_ext.url_route + '.:(format)', mapper.connect(req_ext.url_route + '.:(format)',
action='process', action='process',
controller=controller, controller=controller,
conditions=resp_ext.conditions) conditions=req_ext.conditions)
mapper.connect(resp_ext.url_route, mapper.connect(req_ext.url_route,
action='process', action='process',
controller=controller, controller=controller,
conditions=resp_ext.conditions) conditions=req_ext.conditions)
response_ext_controllers[resp_ext.key] = controller request_ext_controllers[req_ext.key] = controller
return response_ext_controllers return request_ext_controllers
def __init__(self, application, ext_mgr=None): def __init__(self, application, ext_mgr=None):
@ -271,13 +259,13 @@ class ExtensionMiddleware(wsgi.Middleware):
controller = action_controllers[action.collection] controller = action_controllers[action.collection]
controller.add_action(action.action_name, action.handler) controller.add_action(action.action_name, action.handler)
# extended responses # extended requests
resp_controllers = self._response_ext_controllers(application, ext_mgr, req_controllers = self._request_ext_controllers(application, ext_mgr,
mapper) mapper)
for response_ext in ext_mgr.get_response_extensions(): for request_ext in ext_mgr.get_request_extensions():
LOG.debug(_('Extended response: %s'), response_ext.key) LOG.debug(_('Extended request: %s'), request_ext.key)
controller = resp_controllers[response_ext.key] controller = req_controllers[request_ext.key]
controller.add_handler(response_ext.handler) controller.add_handler(request_ext.handler)
self._router = routes.middleware.RoutesMiddleware(self._dispatch, self._router = routes.middleware.RoutesMiddleware(self._dispatch,
mapper) mapper)
@ -347,17 +335,17 @@ class ExtensionManager(object):
pass pass
return actions return actions
def get_response_extensions(self): def get_request_extensions(self):
"""Returns a list of ResponseExtension objects.""" """Returns a list of RequestExtension objects."""
response_exts = [] request_exts = []
for alias, ext in self.extensions.iteritems(): for alias, ext in self.extensions.iteritems():
try: try:
response_exts.extend(ext.get_response_extensions()) request_exts.extend(ext.get_request_extensions())
except AttributeError: except AttributeError:
# NOTE(dprince): Extension aren't required to have response # NOTE(dprince): Extension aren't required to have request
# extensions # extensions
pass pass
return response_exts return request_exts
def _check_extension(self, extension): def _check_extension(self, extension):
"""Checks for required methods in extension objects.""" """Checks for required methods in extension objects."""
@ -421,9 +409,13 @@ class ExtensionManager(object):
self.extensions[alias] = ext self.extensions[alias] = ext
class ResponseExtension(object): class RequestExtension(object):
"""Add data to responses from core nova OpenStack API controllers.""" """Extend requests and responses of core nova OpenStack API controllers.
Provide a way to add data to responses and handle custom request data
that is sent to core nova OpenStack API controllers.
"""
def __init__(self, method, url_route, handler): def __init__(self, method, url_route, handler):
self.url_route = url_route self.url_route = url_route
self.handler = handler self.handler = handler

View File

@ -63,31 +63,33 @@ class Foxinsocks(object):
self._delete_tweedle)) self._delete_tweedle))
return actions return actions
def get_response_extensions(self): def get_request_extensions(self):
response_exts = [] request_exts = []
def _goose_handler(res): def _goose_handler(req, res):
#NOTE: This only handles JSON responses. #NOTE: This only handles JSON responses.
# You can use content type header to test for XML. # You can use content type header to test for XML.
data = json.loads(res.body) data = json.loads(res.body)
data['flavor']['googoose'] = "Gooey goo for chewy chewing!" data['flavor']['googoose'] = req.GET.get('chewing')
return data res.body = json.dumps(data)
return res
resp_ext = extensions.ResponseExtension('GET', '/v1.1/flavors/:(id)', req_ext1 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)',
_goose_handler) _goose_handler)
response_exts.append(resp_ext) request_exts.append(req_ext1)
def _bands_handler(res): def _bands_handler(req, res):
#NOTE: This only handles JSON responses. #NOTE: This only handles JSON responses.
# You can use content type header to test for XML. # You can use content type header to test for XML.
data = json.loads(res.body) data = json.loads(res.body)
data['big_bands'] = 'Pig Bands!' data['big_bands'] = 'Pig Bands!'
return data res.body = json.dumps(data)
return res
resp_ext2 = extensions.ResponseExtension('GET', '/v1.1/flavors/:(id)', req_ext2 = extensions.RequestExtension('GET', '/v1.1/flavors/:(id)',
_bands_handler) _bands_handler)
response_exts.append(resp_ext2) request_exts.append(req_ext2)
return response_exts return request_exts
def _add_tweedle(self, input_dict, req, id): def _add_tweedle(self, input_dict, req, id):

View File

@ -45,10 +45,10 @@ class StubController(nova.wsgi.Controller):
class StubExtensionManager(object): class StubExtensionManager(object):
def __init__(self, resource_ext=None, action_ext=None, response_ext=None): def __init__(self, resource_ext=None, action_ext=None, request_ext=None):
self.resource_ext = resource_ext self.resource_ext = resource_ext
self.action_ext = action_ext self.action_ext = action_ext
self.response_ext = response_ext self.request_ext = request_ext
def get_name(self): def get_name(self):
return "Tweedle Beetle Extension" return "Tweedle Beetle Extension"
@ -71,11 +71,11 @@ class StubExtensionManager(object):
action_exts.append(self.action_ext) action_exts.append(self.action_ext)
return action_exts return action_exts
def get_response_extensions(self): def get_request_extensions(self):
response_exts = [] request_extensions = []
if self.response_ext: if self.request_ext:
response_exts.append(self.response_ext) request_extensions.append(self.request_ext)
return response_exts return request_extensions
class ExtensionControllerTest(unittest.TestCase): class ExtensionControllerTest(unittest.TestCase):
@ -183,10 +183,10 @@ class ActionExtensionTest(unittest.TestCase):
self.assertEqual(404, response.status_int) self.assertEqual(404, response.status_int)
class ResponseExtensionTest(unittest.TestCase): class RequestExtensionTest(unittest.TestCase):
def setUp(self): def setUp(self):
super(ResponseExtensionTest, self).setUp() super(RequestExtensionTest, self).setUp()
self.stubs = stubout.StubOutForTesting() self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.reset_fake_data() fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {} fakes.FakeAuthDatabase.data = {}
@ -195,42 +195,39 @@ class ResponseExtensionTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
self.stubs.UnsetAll() self.stubs.UnsetAll()
super(ResponseExtensionTest, self).tearDown() super(RequestExtensionTest, self).tearDown()
def test_get_resources_with_stub_mgr(self): def test_get_resources_with_stub_mgr(self):
test_resp = "Gooey goo for chewy chewing!" def _req_handler(req, res):
def _resp_handler(res):
# only handle JSON responses # only handle JSON responses
data = json.loads(res.body) data = json.loads(res.body)
data['flavor']['googoose'] = test_resp data['flavor']['googoose'] = req.GET.get('chewing')
return data res.body = json.dumps(data)
return res
resp_ext = extensions.ResponseExtension('GET', req_ext = extensions.RequestExtension('GET',
'/v1.1/flavors/:(id)', '/v1.1/flavors/:(id)',
_resp_handler) _req_handler)
manager = StubExtensionManager(None, None, resp_ext) manager = StubExtensionManager(None, None, req_ext)
app = fakes.wsgi_app() app = fakes.wsgi_app()
ext_midware = extensions.ExtensionMiddleware(app, manager) ext_midware = extensions.ExtensionMiddleware(app, manager)
request = webob.Request.blank("/v1.1/flavors/1") request = webob.Request.blank("/v1.1/flavors/1?chewing=bluegoo")
request.environ['api.version'] = '1.1' request.environ['api.version'] = '1.1'
response = request.get_response(ext_midware) response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int) self.assertEqual(200, response.status_int)
response_data = json.loads(response.body) response_data = json.loads(response.body)
self.assertEqual(test_resp, response_data['flavor']['googoose']) self.assertEqual('bluegoo', response_data['flavor']['googoose'])
def test_get_resources_with_mgr(self): def test_get_resources_with_mgr(self):
test_resp = "Gooey goo for chewy chewing!"
app = fakes.wsgi_app() app = fakes.wsgi_app()
ext_midware = extensions.ExtensionMiddleware(app) ext_midware = extensions.ExtensionMiddleware(app)
request = webob.Request.blank("/v1.1/flavors/1") request = webob.Request.blank("/v1.1/flavors/1?chewing=newblue")
request.environ['api.version'] = '1.1' request.environ['api.version'] = '1.1'
response = request.get_response(ext_midware) response = request.get_response(ext_midware)
self.assertEqual(200, response.status_int) self.assertEqual(200, response.status_int)
response_data = json.loads(response.body) response_data = json.loads(response.body)
self.assertEqual(test_resp, response_data['flavor']['googoose']) self.assertEqual('newblue', response_data['flavor']['googoose'])
self.assertEqual("Pig Bands!", response_data['big_bands']) self.assertEqual("Pig Bands!", response_data['big_bands'])

View File

@ -0,0 +1 @@
1c:87:d1:d9:32:fd:62:3c:78:2b:c0:ad:c0:15:88:df

View File

@ -0,0 +1 @@
ssh-dss AAAAB3NzaC1kc3MAAACBAMGJlY9XEIm2X234pdO5yFWMp2JuOQx8U0E815IVXhmKxYCBK9ZakgZOIQmPbXoGYyV+mziDPp6HJ0wKYLQxkwLEFr51fAZjWQvRss0SinURRuLkockDfGFtD4pYJthekr/rlqMKlBSDUSpGq8jUWW60UJ18FGooFpxR7ESqQRx/AAAAFQC96LRglaUeeP+E8U/yblEJocuiWwAAAIA3XiMR8Skiz/0aBm5K50SeQznQuMJTyzt9S9uaz5QZWiFu69hOyGSFGw8fqgxEkXFJIuHobQQpGYQubLW0NdaYRqyE/Vud3JUJUb8Texld6dz8vGemyB5d1YvtSeHIo8/BGv2msOqR3u5AZTaGCBD9DhpSGOKHEdNjTtvpPd8S8gAAAIBociGZ5jf09iHLVENhyXujJbxfGRPsyNTyARJfCOGl0oFV6hEzcQyw8U/ePwjgvjc2UizMWLl8tsb2FXKHRdc2v+ND3Us+XqKQ33X3ADP4FZ/+Oj213gMyhCmvFTP0u5FmHog9My4CB7YcIWRuUR42WlhQ2IfPvKwUoTk3R+T6Og== www-data@mk

View File

@ -354,6 +354,36 @@ class CloudTestCase(test.TestCase):
self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys)) self.assertTrue(filter(lambda k: k['keyName'] == 'test1', keys))
self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys)) self.assertTrue(filter(lambda k: k['keyName'] == 'test2', keys))
def test_import_public_key(self):
# test when user provides all values
result1 = self.cloud.import_public_key(self.context,
'testimportkey1',
'mytestpubkey',
'mytestfprint')
self.assertTrue(result1)
keydata = db.key_pair_get(self.context,
self.context.user.id,
'testimportkey1')
self.assertEqual('mytestpubkey', keydata['public_key'])
self.assertEqual('mytestfprint', keydata['fingerprint'])
# test when user omits fingerprint
pubkey_path = os.path.join(os.path.dirname(__file__), 'public_key')
f = open(pubkey_path + '/dummy.pub', 'r')
dummypub = f.readline().rstrip()
f.close
f = open(pubkey_path + '/dummy.fingerprint', 'r')
dummyfprint = f.readline().rstrip()
f.close
result2 = self.cloud.import_public_key(self.context,
'testimportkey2',
dummypub)
self.assertTrue(result2)
keydata = db.key_pair_get(self.context,
self.context.user.id,
'testimportkey2')
self.assertEqual(dummypub, keydata['public_key'])
self.assertEqual(dummyfprint, keydata['fingerprint'])
def test_delete_key_pair(self): def test_delete_key_pair(self):
self._create_key('test') self._create_key('test')
self.cloud.delete_key_pair(self.context, 'test') self.cloud.delete_key_pair(self.context, 'test')

View File

@ -81,34 +81,36 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
else: else:
mapped_device = device mapped_device = device
# We can only loopback mount raw images. If the device isn't there,
# it's normally because it's a .vmdk or a .vdi etc
if not os.path.exists(mapped_device):
raise exception.Error('Mapped device was not found (we can'
' only inject raw disk images): %s' %
mapped_device)
# Configure ext2fs so that it doesn't auto-check every N boots
out, err = utils.execute('sudo', 'tune2fs',
'-c', 0, '-i', 0, mapped_device)
tmpdir = tempfile.mkdtemp()
try: try:
# mount loopback to dir # We can only loopback mount raw images. If the device isn't there,
out, err = utils.execute( # it's normally because it's a .vmdk or a .vdi etc
'sudo', 'mount', mapped_device, tmpdir) if not os.path.exists(mapped_device):
if err: raise exception.Error('Mapped device was not found (we can'
raise exception.Error(_('Failed to mount filesystem: %s') ' only inject raw disk images): %s' %
% err) mapped_device)
# Configure ext2fs so that it doesn't auto-check every N boots
out, err = utils.execute('sudo', 'tune2fs',
'-c', 0, '-i', 0, mapped_device)
tmpdir = tempfile.mkdtemp()
try: try:
inject_data_into_fs(tmpdir, key, net, utils.execute) # mount loopback to dir
out, err = utils.execute(
'sudo', 'mount', mapped_device, tmpdir)
if err:
raise exception.Error(_('Failed to mount filesystem: %s')
% err)
try:
inject_data_into_fs(tmpdir, key, net, utils.execute)
finally:
# unmount device
utils.execute('sudo', 'umount', mapped_device)
finally: finally:
# unmount device # remove temporary directory
utils.execute('sudo', 'umount', mapped_device) utils.execute('rmdir', tmpdir)
finally: finally:
# remove temporary directory
utils.execute('rmdir', tmpdir)
if not partition is None: if not partition is None:
# remove partitions # remove partitions
utils.execute('sudo', 'kpartx', '-d', device) utils.execute('sudo', 'kpartx', '-d', device)

View File

@ -21,19 +21,10 @@
Handling of VM disk images. Handling of VM disk images.
""" """
import os.path
import shutil
import sys
import time
import urllib2
import urlparse
from nova import context from nova import context
from nova import flags from nova import flags
from nova import log as logging from nova import log as logging
from nova import utils from nova import utils
from nova.auth import manager
from nova.auth import signer
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@ -52,66 +43,6 @@ def fetch(image_id, path, _user, _project):
return metadata return metadata
# NOTE(vish): The methods below should be unnecessary, but I'm leaving
# them in case the glance client does not work on windows.
def _fetch_image_no_curl(url, path, headers):
request = urllib2.Request(url)
for (k, v) in headers.iteritems():
request.add_header(k, v)
def urlretrieve(urlfile, fpath):
chunk = 1 * 1024 * 1024
f = open(fpath, "wb")
while 1:
data = urlfile.read(chunk)
if not data:
break
f.write(data)
urlopened = urllib2.urlopen(request)
urlretrieve(urlopened, path)
LOG.debug(_("Finished retreving %(url)s -- placed in %(path)s") % locals())
def _fetch_s3_image(image, path, user, project):
url = image_url(image)
# This should probably move somewhere else, like e.g. a download_as
# method on User objects and at the same time get rewritten to use
# a web client.
headers = {}
headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
(_, _, url_path, _, _, _) = urlparse.urlparse(url)
access = manager.AuthManager().get_access_key(user, project)
signature = signer.Signer(user.secret.encode()).s3_authorization(headers,
'GET',
url_path)
headers['Authorization'] = 'AWS %s:%s' % (access, signature)
if sys.platform.startswith('win'):
return _fetch_image_no_curl(url, path, headers)
else:
cmd = ['/usr/bin/curl', '--fail', '--silent', url]
for (k, v) in headers.iteritems():
cmd += ['-H', '\'%s: %s\'' % (k, v)]
cmd += ['-o', path]
return utils.execute(*cmd)
def _fetch_local_image(image, path, user, project):
source = _image_path(os.path.join(image, 'image'))
if sys.platform.startswith('win'):
return shutil.copy(source, path)
else:
return utils.execute('cp', source, path)
def _image_path(path):
return os.path.join(FLAGS.images_path, path)
# TODO(vish): xenapi should use the glance client code directly instead # TODO(vish): xenapi should use the glance client code directly instead
# of retrieving the image using this method. # of retrieving the image using this method.
def image_url(image): def image_url(image):

View File

@ -48,6 +48,8 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('default_os_type', 'linux', 'Default OS type') flags.DEFINE_string('default_os_type', 'linux', 'Default OS type')
flags.DEFINE_integer('block_device_creation_timeout', 10, flags.DEFINE_integer('block_device_creation_timeout', 10,
'time to wait for a block device to be created') 'time to wait for a block device to be created')
flags.DEFINE_integer('max_kernel_ramdisk_size', 16 * 1024 * 1024,
'maximum size in bytes of kernel or ramdisk images')
XENAPI_POWER_STATE = { XENAPI_POWER_STATE = {
'Halted': power_state.SHUTDOWN, 'Halted': power_state.SHUTDOWN,
@ -444,6 +446,12 @@ class VMHelper(HelperBase):
if image_type == ImageType.DISK: if image_type == ImageType.DISK:
# Make room for MBR. # Make room for MBR.
vdi_size += MBR_SIZE_BYTES vdi_size += MBR_SIZE_BYTES
elif image_type == ImageType.KERNEL_RAMDISK and \
vdi_size > FLAGS.max_kernel_ramdisk_size:
max_size = FLAGS.max_kernel_ramdisk_size
raise exception.Error(
_("Kernel/Ramdisk image is too large: %(vdi_size)d bytes, "
"max %(max_size)d bytes") % locals())
name_label = get_name_label_for_image(image) name_label = get_name_label_for_image(image)
vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False) vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False)

View File

@ -25,7 +25,6 @@ import M2Crypto
import os import os
import pickle import pickle
import subprocess import subprocess
import tempfile
import uuid import uuid
from nova import context from nova import context
@ -1163,18 +1162,17 @@ class SimpleDH(object):
return mpi return mpi
def _run_ssl(self, text, which): def _run_ssl(self, text, which):
base_cmd = ('cat %(tmpfile)s | openssl enc -aes-128-cbc ' base_cmd = ('openssl enc -aes-128-cbc -a -pass pass:%(shared)s '
'-a -pass pass:%(shared)s -nosalt %(dec_flag)s') '-nosalt %(dec_flag)s')
if which.lower()[0] == 'd': if which.lower()[0] == 'd':
dec_flag = ' -d' dec_flag = ' -d'
else: else:
dec_flag = '' dec_flag = ''
fd, tmpfile = tempfile.mkstemp()
os.close(fd)
file(tmpfile, 'w').write(text)
shared = self._shared shared = self._shared
cmd = base_cmd % locals() cmd = base_cmd % locals()
proc = _runproc(cmd) proc = _runproc(cmd)
proc.stdin.write(text)
proc.stdin.close()
proc.wait() proc.wait()
err = proc.stderr.read() err = proc.stderr.read()
if err: if err:

View File

@ -59,7 +59,13 @@ function run_tests {
function run_pep8 { function run_pep8 {
echo "Running pep8 ..." echo "Running pep8 ..."
# Opt-out files from pep8
ignore_scripts="*.sh:*nova-debug:*clean-vlans"
ignore_files="*eventlet-patch:*pip-requires"
ignore_dirs="*ajaxterm*"
GLOBIGNORE="$ignore_scripts:$ignore_files:$ignore_dirs"
srcfiles=`find bin -type f ! -name "nova.conf*"` srcfiles=`find bin -type f ! -name "nova.conf*"`
srcfiles+=" `find tools/*`"
srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance"
pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py ${srcfiles} pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py ${srcfiles}
} }

View File

@ -31,119 +31,125 @@ import sys
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
VENV = os.path.join(ROOT, '.nova-venv') VENV = os.path.join(ROOT, '.nova-venv')
PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires') PIP_REQUIRES = os.path.join(ROOT, 'tools', 'pip-requires')
TWISTED_NOVA='http://nova.openstack.org/Twisted-10.0.0Nova.tar.gz' TWISTED_NOVA = 'http://nova.openstack.org/Twisted-10.0.0Nova.tar.gz'
PY_VERSION = "python" + str(sys.version_info[0]) + '.' + str(sys.version_info[1]) PY_VERSION = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
def die(message, *args): def die(message, *args):
print >>sys.stderr, message % args print >>sys.stderr, message % args
sys.exit(1) sys.exit(1)
def check_python_version(): def check_python_version():
if sys.version_info < (2, 6): if sys.version_info < (2, 6):
die("Need Python Version >= 2.6") die("Need Python Version >= 2.6")
def run_command(cmd, redirect_output=True, check_exit_code=True): def run_command(cmd, redirect_output=True, check_exit_code=True):
""" """
Runs a command in an out-of-process shell, returning the Runs a command in an out-of-process shell, returning the
output of that command. Working directory is ROOT. output of that command. Working directory is ROOT.
""" """
if redirect_output: if redirect_output:
stdout = subprocess.PIPE stdout = subprocess.PIPE
else: else:
stdout = None stdout = None
proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout) proc = subprocess.Popen(cmd, cwd=ROOT, stdout=stdout)
output = proc.communicate()[0] output = proc.communicate()[0]
if check_exit_code and proc.returncode != 0: if check_exit_code and proc.returncode != 0:
die('Command "%s" failed.\n%s', ' '.join(cmd), output) die('Command "%s" failed.\n%s', ' '.join(cmd), output)
return output return output
HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], check_exit_code=False).strip()) HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'],
HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], check_exit_code=False).strip()) check_exit_code=False).strip())
HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'],
check_exit_code=False).strip())
def check_dependencies(): def check_dependencies():
"""Make sure virtualenv is in the path.""" """Make sure virtualenv is in the path."""
if not HAS_VIRTUALENV: if not HAS_VIRTUALENV:
print 'not found.' print 'not found.'
# Try installing it via easy_install... # Try installing it via easy_install...
if HAS_EASY_INSTALL: if HAS_EASY_INSTALL:
print 'Installing virtualenv via easy_install...', print 'Installing virtualenv via easy_install...',
if not (run_command(['which', 'easy_install']) and if not (run_command(['which', 'easy_install']) and
run_command(['easy_install', 'virtualenv'])): run_command(['easy_install', 'virtualenv'])):
die('ERROR: virtualenv not found.\n\nNova development requires virtualenv,' die('ERROR: virtualenv not found.\n\nNova development'
' please install it using your favorite package management tool') ' requires virtualenv, please install it using your'
print 'done.' ' favorite package management tool')
print 'done.' print 'done.'
print 'done.'
def create_virtualenv(venv=VENV): def create_virtualenv(venv=VENV):
"""Creates the virtual environment and installs PIP only into the """Creates the virtual environment and installs PIP only into the
virtual environment virtual environment
""" """
print 'Creating venv...', print 'Creating venv...',
run_command(['virtualenv', '-q', '--no-site-packages', VENV]) run_command(['virtualenv', '-q', '--no-site-packages', VENV])
print 'done.' print 'done.'
print 'Installing pip in virtualenv...', print 'Installing pip in virtualenv...',
if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip(): if not run_command(['tools/with_venv.sh', 'easy_install', 'pip']).strip():
die("Failed to install pip.") die("Failed to install pip.")
print 'done.' print 'done.'
def install_dependencies(venv=VENV): def install_dependencies(venv=VENV):
print 'Installing dependencies with pip (this can take a while)...' print 'Installing dependencies with pip (this can take a while)...'
# Install greenlet by hand - just listing it in the requires file does not # Install greenlet by hand - just listing it in the requires file does not
# get it in stalled in the right order # get it in stalled in the right order
run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, 'greenlet'], run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv,
redirect_output=False) 'greenlet'], redirect_output=False)
run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, '-r', PIP_REQUIRES], run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, '-r',
redirect_output=False) PIP_REQUIRES], redirect_output=False)
run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv, TWISTED_NOVA], run_command(['tools/with_venv.sh', 'pip', 'install', '-E', venv,
redirect_output=False) TWISTED_NOVA], redirect_output=False)
# Tell the virtual env how to "import nova"
# Tell the virtual env how to "import nova" pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages",
pthfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", "nova.pth") "nova.pth")
f = open(pthfile, 'w') f = open(pthfile, 'w')
f.write("%s\n" % ROOT) f.write("%s\n" % ROOT)
# Patch eventlet (see FAQ # 1485) # Patch eventlet (see FAQ # 1485)
patchsrc = os.path.join(ROOT, 'tools', 'eventlet-patch') patchsrc = os.path.join(ROOT, 'tools', 'eventlet-patch')
patchfile = os.path.join(venv, "lib", PY_VERSION, "site-packages", "eventlet", patchfile = os.path.join(venv, "lib", PY_VERSION, "site-packages",
"green", "subprocess.py") "eventlet", "green", "subprocess.py")
patch_cmd = "patch %s %s" % (patchfile, patchsrc) patch_cmd = "patch %s %s" % (patchfile, patchsrc)
os.system(patch_cmd) os.system(patch_cmd)
def print_help(): def print_help():
help = """ help = """
Nova development environment setup is complete. Nova development environment setup is complete.
Nova development uses virtualenv to track and manage Python dependencies Nova development uses virtualenv to track and manage Python dependencies
while in development and testing. while in development and testing.
To activate the Nova virtualenv for the extent of your current shell session To activate the Nova virtualenv for the extent of your current shell
you can run: session you can run:
$ source .nova-venv/bin/activate $ source .nova-venv/bin/activate
Or, if you prefer, you can run commands in the virtualenv on a case by case Or, if you prefer, you can run commands in the virtualenv on a case by case
basis by running: basis by running:
$ tools/with_venv.sh <your command> $ tools/with_venv.sh <your command>
Also, make test will automatically use the virtualenv. Also, make test will automatically use the virtualenv.
""" """
print help print help
def main(argv): def main(argv):
check_python_version() check_python_version()
check_dependencies() check_dependencies()
create_virtualenv() create_virtualenv()
install_dependencies() install_dependencies()
print_help() print_help()
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv) main(sys.argv)