Fix bug 921814 changes handling of adminPass in API.
Add a new nova configuration flag, boolean, enable_instance_password. When the flag is True (default), existing behavior is unchanged. When the flag is False, responses from the create or rebuild API calls don't include the adminPass attribute. Change-Id: Icb2bd703770f3a39bb1e458dc31e1489d48da7c1
This commit is contained in:
1
Authors
1
Authors
@@ -115,6 +115,7 @@ Matthew Hooker <matt@cloudscaling.com>
|
||||
Michael Gundlach <michael.gundlach@rackspace.com>
|
||||
Michael Still <mikal@stillhq.com>
|
||||
Mike Lundy <mike@pistoncloud.com>
|
||||
Mike Pittaro <mikeyp@lahondaresearch.org>
|
||||
Mike Scherbakov <mihgen@gmail.com>
|
||||
Mikyung Kang <mkkang@isi.edu>
|
||||
Mohammed Naser <mnaser@vexxhost.com>
|
||||
|
@@ -784,7 +784,8 @@ class Controller(wsgi.Controller):
|
||||
if '_is_precooked' in server['server'].keys():
|
||||
del server['server']['_is_precooked']
|
||||
else:
|
||||
server['server']['adminPass'] = password
|
||||
if FLAGS.enable_instance_password:
|
||||
server['server']['adminPass'] = password
|
||||
|
||||
robj = wsgi.ResponseObject(server)
|
||||
|
||||
@@ -1107,7 +1108,9 @@ class Controller(wsgi.Controller):
|
||||
view = self._view_builder.show(req, instance)
|
||||
|
||||
# Add on the adminPass attribute since the view doesn't do it
|
||||
view['server']['adminPass'] = password
|
||||
# unless instance passwords are disabled
|
||||
if FLAGS.enable_instance_password:
|
||||
view['server']['adminPass'] = password
|
||||
|
||||
robj = wsgi.ResponseObject(view)
|
||||
return self._add_location(robj)
|
||||
|
@@ -503,6 +503,10 @@ global_opts = [
|
||||
cfg.BoolOpt('use_ipv6',
|
||||
default=False,
|
||||
help='use ipv6'),
|
||||
cfg.BoolOpt('enable_instance_password',
|
||||
default=True,
|
||||
help='Allows use of instance password during '
|
||||
'server creation'),
|
||||
cfg.IntOpt('password_length',
|
||||
default=12,
|
||||
help='Length of generated instance admin passwords'),
|
||||
|
@@ -165,7 +165,8 @@ class ServerActionsControllerTest(test.TestCase):
|
||||
self.service.delete_all()
|
||||
self.sent_to_glance = {}
|
||||
fakes.stub_out_glance_add_image(self.stubs, self.sent_to_glance)
|
||||
self.flags(allow_instance_snapshots=True)
|
||||
self.flags(allow_instance_snapshots=True,
|
||||
enable_instance_password=True)
|
||||
self.uuid = FAKE_UUID
|
||||
self.url = '/v2/fake/servers/%s/action' % self.uuid
|
||||
self._image_href = '155d900f-4e14-4e4c-a73d-069cbf4541e6'
|
||||
@@ -187,6 +188,22 @@ class ServerActionsControllerTest(test.TestCase):
|
||||
self.assertEqual(mock_method.instance_id, self.uuid)
|
||||
self.assertEqual(mock_method.password, '1234pass')
|
||||
|
||||
def test_server_change_password_pass_disabled(self):
|
||||
# run with enable_instance_password disabled to verify adminPass
|
||||
# is missing from response. See lp bug 921814
|
||||
self.flags(enable_instance_password=False)
|
||||
|
||||
mock_method = MockSetAdminPassword()
|
||||
self.stubs.Set(nova.compute.api.API, 'set_admin_password', mock_method)
|
||||
body = {'changePassword': {'adminPass': '1234pass'}}
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
self.controller._action_change_password(req, FAKE_UUID, body)
|
||||
|
||||
self.assertEqual(mock_method.instance_id, self.uuid)
|
||||
# note,the mock still contains the password.
|
||||
self.assertEqual(mock_method.password, '1234pass')
|
||||
|
||||
def test_server_change_password_not_a_string(self):
|
||||
body = {'changePassword': {'adminPass': 1234}}
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
@@ -280,6 +297,31 @@ class ServerActionsControllerTest(test.TestCase):
|
||||
self.assertEqual(body['server']['image']['id'], '2')
|
||||
self.assertEqual(len(body['server']['adminPass']),
|
||||
FLAGS.password_length)
|
||||
|
||||
self.assertEqual(robj['location'], self_href)
|
||||
|
||||
def test_rebuild_accepted_minimum_pass_disabled(self):
|
||||
# run with enable_instance_password disabled to verify adminPass
|
||||
# is missing from response. See lp bug 921814
|
||||
self.flags(enable_instance_password=False)
|
||||
|
||||
new_return_server = return_server_with_attributes(image_ref='2')
|
||||
self.stubs.Set(nova.db, 'instance_get', new_return_server)
|
||||
self_href = 'http://localhost/v2/fake/servers/%s' % FAKE_UUID
|
||||
|
||||
body = {
|
||||
"rebuild": {
|
||||
"imageRef": self._image_href,
|
||||
},
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
robj = self.controller._action_rebuild(req, FAKE_UUID, body)
|
||||
body = robj.obj
|
||||
|
||||
self.assertEqual(body['server']['image']['id'], '2')
|
||||
self.assertTrue("adminPass" not in body['server'])
|
||||
|
||||
self.assertEqual(robj['location'], self_href)
|
||||
|
||||
def test_rebuild_raises_conflict_on_invalid_state(self):
|
||||
@@ -391,6 +433,27 @@ class ServerActionsControllerTest(test.TestCase):
|
||||
self.assertEqual(body['server']['image']['id'], '2')
|
||||
self.assertEqual(body['server']['adminPass'], 'asdf')
|
||||
|
||||
def test_rebuild_admin_pass_pass_disabled(self):
|
||||
# run with enable_instance_password disabled to verify adminPass
|
||||
# is missing from response. See lp bug 921814
|
||||
self.flags(enable_instance_password=False)
|
||||
|
||||
new_return_server = return_server_with_attributes(image_ref='2')
|
||||
self.stubs.Set(nova.db, 'instance_get', new_return_server)
|
||||
|
||||
body = {
|
||||
"rebuild": {
|
||||
"imageRef": self._image_href,
|
||||
"adminPass": "asdf",
|
||||
},
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank(self.url)
|
||||
body = self.controller._action_rebuild(req, FAKE_UUID, body).obj
|
||||
|
||||
self.assertEqual(body['server']['image']['id'], '2')
|
||||
self.assertTrue('adminPass' not in body['server'])
|
||||
|
||||
def test_rebuild_server_not_found(self):
|
||||
def server_not_found(self, instance_id):
|
||||
raise exception.InstanceNotFound(instance_id=instance_id)
|
||||
|
@@ -1393,7 +1393,8 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
super(ServersControllerCreateTest, self).setUp()
|
||||
|
||||
self.maxDiff = None
|
||||
self.flags(verbose=True)
|
||||
self.flags(verbose=True,
|
||||
enable_instance_password=True)
|
||||
self.config_drive = None
|
||||
self.instance_cache_num = 0
|
||||
self.instance_cache = {}
|
||||
@@ -1475,6 +1476,20 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
self.stubs.Set(nova.network.manager.VlanManager, 'allocate_fixed_ip',
|
||||
fake_method)
|
||||
|
||||
def _check_admin_pass_len(self, server_dict):
|
||||
""" utility function - check server_dict for adminPass
|
||||
length.
|
||||
|
||||
"""
|
||||
self.assertEqual(FLAGS.password_length,
|
||||
len(server_dict["adminPass"]))
|
||||
|
||||
def _check_admin_pass_missing(self, server_dict):
|
||||
""" utility function - check server_dict for absence
|
||||
of adminPass
|
||||
"""
|
||||
self.assertTrue("adminPass" not in server_dict)
|
||||
|
||||
def _test_create_instance(self):
|
||||
image_uuid = 'c905cedb-7281-47e4-8a62-f26bc5fc4c77'
|
||||
body = dict(server=dict(
|
||||
@@ -1487,7 +1502,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
req.headers["content-type"] = "application/json"
|
||||
server = self.controller.create(req, body).obj['server']
|
||||
|
||||
self.assertEqual(FLAGS.password_length, len(server['adminPass']))
|
||||
self._check_admin_pass_len(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
def test_create_multiple_instances(self):
|
||||
@@ -1515,7 +1530,35 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
self.assertEqual(FAKE_UUID, res["server"]["id"])
|
||||
self.assertEqual(12, len(res["server"]["adminPass"]))
|
||||
self._check_admin_pass_len(res["server"])
|
||||
|
||||
def test_create_multiple_instances_pass_disabled(self):
|
||||
"""Test creating multiple instances but not asking for
|
||||
reservation_id
|
||||
"""
|
||||
self.flags(enable_instance_password=False)
|
||||
image_href = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
body = {
|
||||
'server': {
|
||||
'min_count': 2,
|
||||
'name': 'server_test',
|
||||
'imageRef': image_href,
|
||||
'flavorRef': flavor_ref,
|
||||
'metadata': {'hello': 'world',
|
||||
'open': 'stack'},
|
||||
'personality': []
|
||||
}
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
self.assertEqual(FAKE_UUID, res["server"]["id"])
|
||||
self._check_admin_pass_missing(res["server"])
|
||||
|
||||
def test_create_multiple_instances_resv_id_return(self):
|
||||
"""Test creating multiple instances with asking for
|
||||
@@ -1678,7 +1721,47 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
server = res['server']
|
||||
self.assertEqual(FLAGS.password_length, len(server['adminPass']))
|
||||
self._check_admin_pass_len(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
def test_create_instance_with_access_ip_pass_disabled(self):
|
||||
# test with admin passwords disabled See lp bug 921814
|
||||
self.flags(enable_instance_password=False)
|
||||
|
||||
# proper local hrefs must start with 'http://localhost/v2/'
|
||||
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
image_href = 'http://localhost/v2/fake/images/%s' % image_uuid
|
||||
flavor_ref = 'http://localhost/fake/flavors/3'
|
||||
access_ipv4 = '1.2.3.4'
|
||||
access_ipv6 = 'fead::1234'
|
||||
body = {
|
||||
'server': {
|
||||
'name': 'server_test',
|
||||
'imageRef': image_href,
|
||||
'flavorRef': flavor_ref,
|
||||
'accessIPv4': access_ipv4,
|
||||
'accessIPv6': access_ipv6,
|
||||
'metadata': {
|
||||
'hello': 'world',
|
||||
'open': 'stack',
|
||||
},
|
||||
'personality': [
|
||||
{
|
||||
"path": "/etc/banner.txt",
|
||||
"contents": "MQ==",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
server = res['server']
|
||||
self._check_admin_pass_missing(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
def test_create_instance_bad_format_access_ip_v4(self):
|
||||
@@ -1768,6 +1851,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
"path": "/etc/banner.txt",
|
||||
"contents": "MQ==",
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1779,7 +1863,42 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
server = res['server']
|
||||
self.assertEqual(FLAGS.password_length, len(server['adminPass']))
|
||||
self._check_admin_pass_len(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
def test_create_instance_pass_disabled(self):
|
||||
self.flags(enable_instance_password=False)
|
||||
# proper local hrefs must start with 'http://localhost/v2/'
|
||||
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
image_href = 'http://localhost/v2/images/%s' % image_uuid
|
||||
flavor_ref = 'http://localhost/123/flavors/3'
|
||||
body = {
|
||||
'server': {
|
||||
'name': 'server_test',
|
||||
'imageRef': image_href,
|
||||
'flavorRef': flavor_ref,
|
||||
'metadata': {
|
||||
'hello': 'world',
|
||||
'open': 'stack',
|
||||
},
|
||||
'personality': [
|
||||
{
|
||||
"path": "/etc/banner.txt",
|
||||
"contents": "MQ==",
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers["content-type"] = "application/json"
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
server = res['server']
|
||||
self._check_admin_pass_missing(server)
|
||||
self.assertEqual(FAKE_UUID, server['id'])
|
||||
|
||||
def test_create_instance_too_much_metadata(self):
|
||||
@@ -1835,7 +1954,7 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
self.assertEqual(FAKE_UUID, res["server"]["id"])
|
||||
self.assertEqual(12, len(res["server"]["adminPass"]))
|
||||
self._check_admin_pass_len(res["server"])
|
||||
|
||||
def test_create_instance_invalid_flavor_href(self):
|
||||
image_href = 'http://localhost/v2/images/2'
|
||||
@@ -2043,6 +2162,28 @@ class ServersControllerCreateTest(test.TestCase):
|
||||
server = res['server']
|
||||
self.assertEqual(server['adminPass'], body['server']['adminPass'])
|
||||
|
||||
def test_create_instance_admin_pass_pass_disabled(self):
|
||||
self.flags(enable_instance_password=False)
|
||||
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
|
||||
body = {
|
||||
'server': {
|
||||
'name': 'server_test',
|
||||
'imageRef': image_uuid,
|
||||
'flavorRef': 3,
|
||||
'adminPass': 'testpass',
|
||||
},
|
||||
}
|
||||
|
||||
req = fakes.HTTPRequest.blank('/v2/fake/servers')
|
||||
req.method = 'POST'
|
||||
req.body = json.dumps(body)
|
||||
req.headers['content-type'] = "application/json"
|
||||
res = self.controller.create(req, body).obj
|
||||
|
||||
server = res['server']
|
||||
self.assertTrue('adminPass' in body['server'] )
|
||||
self.assertTrue('adminPass' not in server)
|
||||
|
||||
def test_create_instance_admin_pass_empty(self):
|
||||
body = {
|
||||
'server': {
|
||||
|
Reference in New Issue
Block a user