Merge lp:~james-page/charms/precise/nova-compute/resize-fixes into lp:~charmers/charms/precise/nova-compute/trunk

Proposed by James Page
Status: Merged
Merged at revision: 54
Proposed branch: lp:~james-page/charms/precise/nova-compute/resize-fixes
Merge into: lp:~charmers/charms/precise/nova-compute/trunk
Diff against target: 285 lines (+146/-23)
5 files modified
config.yaml (+5/-0)
hooks/nova_compute_hooks.py (+24/-11)
hooks/nova_compute_utils.py (+19/-4)
unit_tests/test_nova_compute_hooks.py (+54/-7)
unit_tests/test_nova_compute_utils.py (+44/-1)
To merge this branch: bzr merge lp:~james-page/charms/precise/nova-compute/resize-fixes
Reviewer Review Type Date Requested Status
Jonathan Davies (community) Approve
Adam Gandelman (community) Approve
Marco Ceppi (community) Abstain
Review via email: mp+199266@code.launchpad.net

Description of the change

Add feature to support resizing of instances; this required SSH access for the nova user between compute hosts.

To post a comment you must log in.
Revision history for this message
Marco Ceppi (marcoceppi) wrote :

Deferring to openstack-charmers

review: Abstain
Revision history for this message
Adam Gandelman (gandelman-a) :
review: Approve
Revision history for this message
Jonathan Davies (jpds) wrote :

+1 I need this for instance migration.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'config.yaml'
--- config.yaml 2013-10-17 19:25:15 +0000
+++ config.yaml 2013-12-17 11:22:05 +0000
@@ -50,6 +50,11 @@
50 default: "yes"50 default: "yes"
51 type: string51 type: string
52 description: Whether to run nova-api and nova-network on the compute nodes.52 description: Whether to run nova-api and nova-network on the compute nodes.
53 enable-resize:
54 default: False
55 type: boolean
56 description: Enable instance resizing, which requires that passwordless SSH
57 access be setup between compute hosts.
53 enable-live-migration:58 enable-live-migration:
54 default: False59 default: False
55 type: boolean60 type: boolean
5661
=== modified file 'hooks/nova_compute_hooks.py'
--- hooks/nova_compute_hooks.py 2013-11-08 05:48:58 +0000
+++ hooks/nova_compute_hooks.py 2013-12-17 11:22:05 +0000
@@ -48,7 +48,8 @@
48 register_configs,48 register_configs,
49 NOVA_CONF,49 NOVA_CONF,
50 QUANTUM_CONF, NEUTRON_CONF,50 QUANTUM_CONF, NEUTRON_CONF,
51 ceph_config_file, CEPH_SECRET51 ceph_config_file, CEPH_SECRET,
52 enable_shell, disable_shell
52)53)
5354
54from nova_compute_context import CEPH_SECRET_UUID55from nova_compute_context import CEPH_SECRET_UUID
@@ -75,7 +76,14 @@
75 # Check-in with nova-c-c and register new ssh key, if it has just been76 # Check-in with nova-c-c and register new ssh key, if it has just been
76 # generated.77 # generated.
77 initialize_ssh_keys()78 initialize_ssh_keys()
78 [compute_joined(rid) for rid in relation_ids('cloud-compute')]79
80 if config('enable-resize') is True:
81 enable_shell(user='nova')
82 initialize_ssh_keys(user='nova')
83 else:
84 disable_shell(user='nova')
85
86 [compute_joined(rid) for rid in relation_ids('cloud-compute')]
7987
80 CONFIGS.write_all()88 CONFIGS.write_all()
8189
@@ -140,15 +148,19 @@
140148
141@hooks.hook('cloud-compute-relation-joined')149@hooks.hook('cloud-compute-relation-joined')
142def compute_joined(rid=None):150def compute_joined(rid=None):
143 if not migration_enabled():151 if migration_enabled():
144 return152 auth_type = config('migration-auth-type')
145 auth_type = config('migration-auth-type')153 settings = {
146 settings = {154 'migration_auth_type': auth_type
147 'migration_auth_type': auth_type155 }
148 }156 if auth_type == 'ssh':
149 if auth_type == 'ssh':157 settings['ssh_public_key'] = public_ssh_key()
150 settings['ssh_public_key'] = public_ssh_key()158 relation_set(relation_id=rid, **settings)
151 relation_set(relation_id=rid, **settings)159 if config('enable-resize'):
160 settings = {
161 'nova_ssh_public_key': public_ssh_key(user='nova')
162 }
163 relation_set(relation_id=rid, **settings)
152164
153165
154@hooks.hook('cloud-compute-relation-changed')166@hooks.hook('cloud-compute-relation-changed')
@@ -158,6 +170,7 @@
158 # config advertised from controller.170 # config advertised from controller.
159 CONFIGS.write_all()171 CONFIGS.write_all()
160 import_authorized_keys()172 import_authorized_keys()
173 import_authorized_keys(user='nova', prefix='nova')
161 import_keystone_ca_cert()174 import_keystone_ca_cert()
162 if (network_manager() in ['quantum', 'neutron']175 if (network_manager() in ['quantum', 'neutron']
163 and neutron_plugin() == 'ovs'):176 and neutron_plugin() == 'ovs'):
164177
=== modified file 'hooks/nova_compute_utils.py'
--- hooks/nova_compute_utils.py 2013-11-01 16:11:43 +0000
+++ hooks/nova_compute_utils.py 2013-12-17 11:22:05 +0000
@@ -310,13 +310,18 @@
310 check_output(['chown', '-R', user, ssh_dir])310 check_output(['chown', '-R', user, ssh_dir])
311311
312312
313def import_authorized_keys(user='root'):313def import_authorized_keys(user='root', prefix=None):
314 """Import SSH authorized_keys + known_hosts from a cloud-compute relation314 """Import SSH authorized_keys + known_hosts from a cloud-compute relation
315 and store in user's $HOME/.ssh.315 and store in user's $HOME/.ssh.
316 """316 """
317 # XXX: Should this be managed via templates + contexts?317 if prefix:
318 hosts = relation_get('known_hosts')318 hosts = relation_get('{}_known_hosts'.format(prefix))
319 auth_keys = relation_get('authorized_keys')319 auth_keys = relation_get('{}_authorized_keys'.format(prefix))
320 else:
321 # XXX: Should this be managed via templates + contexts?
322 hosts = relation_get('known_hosts')
323 auth_keys = relation_get('authorized_keys')
324
320 # XXX: Need to fix charm-helpers to return None for empty settings,325 # XXX: Need to fix charm-helpers to return None for empty settings,
321 # in all cases.326 # in all cases.
322 if not hosts or not auth_keys:327 if not hosts or not auth_keys:
@@ -394,3 +399,13 @@
394 cmd = ['virsh', 'secret-set-value', '--secret', secret_uuid,399 cmd = ['virsh', 'secret-set-value', '--secret', secret_uuid,
395 '--base64', key]400 '--base64', key]
396 check_call(cmd)401 check_call(cmd)
402
403
404def enable_shell(user):
405 cmd = ['usermod', '-s', '/bin/bash', user]
406 check_call(cmd)
407
408
409def disable_shell(user):
410 cmd = ['usermod', '-s', '/bin/false', user]
411 check_call(cmd)
397412
=== modified file 'unit_tests/test_nova_compute_hooks.py'
--- unit_tests/test_nova_compute_hooks.py 2013-11-17 21:47:29 +0000
+++ unit_tests/test_nova_compute_hooks.py 2013-12-17 11:22:05 +0000
@@ -48,6 +48,8 @@
48 'neutron_plugin',48 'neutron_plugin',
49 'public_ssh_key',49 'public_ssh_key',
50 'register_configs',50 'register_configs',
51 'disable_shell',
52 'enable_shell',
51 # misc_utils53 # misc_utils
52 'ensure_ceph_keyring',54 'ensure_ceph_keyring',
53 'execd_preinstall'55 'execd_preinstall'
@@ -95,6 +97,38 @@
95 call('cloud-compute:1'),97 call('cloud-compute:1'),
96 ]98 ]
97 self.assertEquals(ex, compute_joined.call_args_list)99 self.assertEquals(ex, compute_joined.call_args_list)
100 self.assertTrue(self.initialize_ssh_keys.called)
101
102 @patch.object(hooks, 'compute_joined')
103 def test_config_changed_with_resize(self, compute_joined):
104 self.test_config.set('enable-resize', True)
105 self.relation_ids.return_value = [
106 'cloud-compute:0',
107 'cloud-compute:1'
108 ]
109 hooks.config_changed()
110 ex = [
111 call('cloud-compute:0'),
112 call('cloud-compute:1'),
113 ]
114 self.assertEquals(ex, compute_joined.call_args_list)
115 self.initialize_ssh_keys.assert_called_with(user='nova')
116 self.enable_shell.assert_called_with(user='nova')
117
118 @patch.object(hooks, 'compute_joined')
119 def test_config_changed_without_resize(self, compute_joined):
120 self.test_config.set('enable-resize', False)
121 self.relation_ids.return_value = [
122 'cloud-compute:0',
123 'cloud-compute:1'
124 ]
125 hooks.config_changed()
126 ex = [
127 call('cloud-compute:0'),
128 call('cloud-compute:1'),
129 ]
130 self.assertEquals(ex, compute_joined.call_args_list)
131 self.disable_shell.assert_called_with(user='nova')
98132
99 @patch.object(hooks, 'compute_joined')133 @patch.object(hooks, 'compute_joined')
100 def test_config_changed_no_upgrade_no_migration(self, compute_joined):134 def test_config_changed_no_upgrade_no_migration(self, compute_joined):
@@ -239,7 +273,7 @@
239 hooks.image_service_changed()273 hooks.image_service_changed()
240 configs.write.assert_called_with('/etc/nova/nova.conf')274 configs.write.assert_called_with('/etc/nova/nova.conf')
241275
242 def test_compute_joined_no_migration(self):276 def test_compute_joined_no_migration_no_resize(self):
243 self.migration_enabled.return_value = False277 self.migration_enabled.return_value = False
244 hooks.compute_joined()278 hooks.compute_joined()
245 self.assertFalse(self.relation_set.called)279 self.assertFalse(self.relation_set.called)
@@ -261,14 +295,27 @@
261 migration_auth_type='ssh'295 migration_auth_type='ssh'
262 )296 )
263297
298 def test_compute_joined_with_resize(self):
299 self.test_config.set('enable-resize', True)
300 self.public_ssh_key.return_value = 'bar'
301 hooks.compute_joined()
302 self.relation_set.assert_called_with(
303 relation_id=None,
304 nova_ssh_public_key='bar'
305 )
306 hooks.compute_joined(rid='cloud-compute:2')
307 self.relation_set.assert_called_with(
308 relation_id='cloud-compute:2',
309 nova_ssh_public_key='bar'
310 )
311
264 def test_compute_changed(self):312 def test_compute_changed(self):
265 hooks.compute_changed()313 hooks.compute_changed()
266 expected_funcs = [314 self.assertTrue(self.import_keystone_ca_cert.called)
267 self.import_authorized_keys,315 self.import_authorized_keys.assert_has_calls([
268 self.import_keystone_ca_cert,316 call(),
269 ]317 call(user='nova', prefix='nova'),
270 for func in expected_funcs:318 ])
271 self.assertTrue(func.called)
272319
273 def test_ceph_joined(self):320 def test_ceph_joined(self):
274 hooks.ceph_joined()321 hooks.ceph_joined()
275322
=== modified file 'unit_tests/test_nova_compute_utils.py'
--- unit_tests/test_nova_compute_utils.py 2013-11-17 21:47:29 +0000
+++ unit_tests/test_nova_compute_utils.py 2013-12-17 11:22:05 +0000
@@ -179,7 +179,7 @@
179 self.assertFalse(_open.called)179 self.assertFalse(_open.called)
180180
181 @patch('pwd.getpwnam')181 @patch('pwd.getpwnam')
182 def test_import_authorized_keys(self, getpwnam):182 def _test_import_authorized_keys_base(self, getpwnam, prefix=None):
183 getpwnam.return_value = self.fake_user('foo')183 getpwnam.return_value = self.fake_user('foo')
184 self.relation_get.side_effect = [184 self.relation_get.side_effect = [
185 'Zm9vX2tleQo=', # relation_get('known_hosts')185 'Zm9vX2tleQo=', # relation_get('known_hosts')
@@ -200,6 +200,38 @@
200 self.assertEquals(ex_open, _open.call_args_list)200 self.assertEquals(ex_open, _open.call_args_list)
201 self.assertEquals(ex_write, _file.write.call_args_list)201 self.assertEquals(ex_write, _file.write.call_args_list)
202202
203 self.relation_get.assert_has_called([
204 call('known_hosts').
205 call('authorized_keys')
206 ])
207
208 @patch('pwd.getpwnam')
209 def test_import_authorized_keys_prefix(self, getpwnam):
210 getpwnam.return_value = self.fake_user('foo')
211 self.relation_get.side_effect = [
212 'Zm9vX2tleQo=', # relation_get('known_hosts')
213 'Zm9vX2hvc3QK', # relation_get('authorized_keys')
214 ]
215
216 ex_open = [
217 call('/home/foo/.ssh/authorized_keys', 'wb'),
218 call('/home/foo/.ssh/known_hosts', 'wb')
219 ]
220 ex_write = [
221 call('foo_host\n'),
222 call('foo_key\n'),
223 ]
224
225 with patch_open() as (_open, _file):
226 utils.import_authorized_keys(user='foo', prefix='bar')
227 self.assertEquals(ex_open, _open.call_args_list)
228 self.assertEquals(ex_write, _file.write.call_args_list)
229
230 self.relation_get.assert_has_called([
231 call('bar_known_hosts').
232 call('bar_authorized_keys')
233 ])
234
203 @patch('subprocess.check_call')235 @patch('subprocess.check_call')
204 def test_import_keystone_cert_missing_data(self, check_call):236 def test_import_keystone_cert_missing_data(self, check_call):
205 self.relation_get.return_value = None237 self.relation_get.return_value = None
@@ -247,3 +279,14 @@
247 call('/etc/nova/nova.conf', [ctxt1])279 call('/etc/nova/nova.conf', [ctxt1])
248 ]280 ]
249 self.assertEquals(fake_renderer.register.call_args_list, ex_reg)281 self.assertEquals(fake_renderer.register.call_args_list, ex_reg)
282
283 @patch.object(utils, 'check_call')
284 def test_enable_shell(self, _check_call):
285 utils.enable_shell('dummy')
286 _check_call.assert_called_with(['usermod', '-s', '/bin/bash', 'dummy'])
287
288 @patch.object(utils, 'check_call')
289 def test_disable_shell(self, _check_call):
290 utils.disable_shell('dummy')
291 _check_call.assert_called_with(['usermod', '-s', '/bin/false',
292 'dummy'])

Subscribers

People subscribed via source and target branches

to all changes: