Merging trunk
This commit is contained in:
commit
821ee03ced
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
@ -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'])
|
||||||
|
1
nova/tests/public_key/dummy.fingerprint
Normal file
1
nova/tests/public_key/dummy.fingerprint
Normal file
@ -0,0 +1 @@
|
|||||||
|
1c:87:d1:d9:32:fd:62:3c:78:2b:c0:ad:c0:15:88:df
|
1
nova/tests/public_key/dummy.pub
Normal file
1
nova/tests/public_key/dummy.pub
Normal file
@ -0,0 +1 @@
|
|||||||
|
ssh-dss AAAAB3NzaC1kc3MAAACBAMGJlY9XEIm2X234pdO5yFWMp2JuOQx8U0E815IVXhmKxYCBK9ZakgZOIQmPbXoGYyV+mziDPp6HJ0wKYLQxkwLEFr51fAZjWQvRss0SinURRuLkockDfGFtD4pYJthekr/rlqMKlBSDUSpGq8jUWW60UJ18FGooFpxR7ESqQRx/AAAAFQC96LRglaUeeP+E8U/yblEJocuiWwAAAIA3XiMR8Skiz/0aBm5K50SeQznQuMJTyzt9S9uaz5QZWiFu69hOyGSFGw8fqgxEkXFJIuHobQQpGYQubLW0NdaYRqyE/Vud3JUJUb8Texld6dz8vGemyB5d1YvtSeHIo8/BGv2msOqR3u5AZTaGCBD9DhpSGOKHEdNjTtvpPd8S8gAAAIBociGZ5jf09iHLVENhyXujJbxfGRPsyNTyARJfCOGl0oFV6hEzcQyw8U/ePwjgvjc2UizMWLl8tsb2FXKHRdc2v+ND3Us+XqKQ33X3ADP4FZ/+Oj213gMyhCmvFTP0u5FmHog9My4CB7YcIWRuUR42WlhQ2IfPvKwUoTk3R+T6Og== www-data@mk
|
@ -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')
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user