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
1=== modified file 'config.yaml'
2--- config.yaml 2013-10-17 19:25:15 +0000
3+++ config.yaml 2013-12-17 11:22:05 +0000
4@@ -50,6 +50,11 @@
5 default: "yes"
6 type: string
7 description: Whether to run nova-api and nova-network on the compute nodes.
8+ enable-resize:
9+ default: False
10+ type: boolean
11+ description: Enable instance resizing, which requires that passwordless SSH
12+ access be setup between compute hosts.
13 enable-live-migration:
14 default: False
15 type: boolean
16
17=== modified file 'hooks/nova_compute_hooks.py'
18--- hooks/nova_compute_hooks.py 2013-11-08 05:48:58 +0000
19+++ hooks/nova_compute_hooks.py 2013-12-17 11:22:05 +0000
20@@ -48,7 +48,8 @@
21 register_configs,
22 NOVA_CONF,
23 QUANTUM_CONF, NEUTRON_CONF,
24- ceph_config_file, CEPH_SECRET
25+ ceph_config_file, CEPH_SECRET,
26+ enable_shell, disable_shell
27 )
28
29 from nova_compute_context import CEPH_SECRET_UUID
30@@ -75,7 +76,14 @@
31 # Check-in with nova-c-c and register new ssh key, if it has just been
32 # generated.
33 initialize_ssh_keys()
34- [compute_joined(rid) for rid in relation_ids('cloud-compute')]
35+
36+ if config('enable-resize') is True:
37+ enable_shell(user='nova')
38+ initialize_ssh_keys(user='nova')
39+ else:
40+ disable_shell(user='nova')
41+
42+ [compute_joined(rid) for rid in relation_ids('cloud-compute')]
43
44 CONFIGS.write_all()
45
46@@ -140,15 +148,19 @@
47
48 @hooks.hook('cloud-compute-relation-joined')
49 def compute_joined(rid=None):
50- if not migration_enabled():
51- return
52- auth_type = config('migration-auth-type')
53- settings = {
54- 'migration_auth_type': auth_type
55- }
56- if auth_type == 'ssh':
57- settings['ssh_public_key'] = public_ssh_key()
58- relation_set(relation_id=rid, **settings)
59+ if migration_enabled():
60+ auth_type = config('migration-auth-type')
61+ settings = {
62+ 'migration_auth_type': auth_type
63+ }
64+ if auth_type == 'ssh':
65+ settings['ssh_public_key'] = public_ssh_key()
66+ relation_set(relation_id=rid, **settings)
67+ if config('enable-resize'):
68+ settings = {
69+ 'nova_ssh_public_key': public_ssh_key(user='nova')
70+ }
71+ relation_set(relation_id=rid, **settings)
72
73
74 @hooks.hook('cloud-compute-relation-changed')
75@@ -158,6 +170,7 @@
76 # config advertised from controller.
77 CONFIGS.write_all()
78 import_authorized_keys()
79+ import_authorized_keys(user='nova', prefix='nova')
80 import_keystone_ca_cert()
81 if (network_manager() in ['quantum', 'neutron']
82 and neutron_plugin() == 'ovs'):
83
84=== modified file 'hooks/nova_compute_utils.py'
85--- hooks/nova_compute_utils.py 2013-11-01 16:11:43 +0000
86+++ hooks/nova_compute_utils.py 2013-12-17 11:22:05 +0000
87@@ -310,13 +310,18 @@
88 check_output(['chown', '-R', user, ssh_dir])
89
90
91-def import_authorized_keys(user='root'):
92+def import_authorized_keys(user='root', prefix=None):
93 """Import SSH authorized_keys + known_hosts from a cloud-compute relation
94 and store in user's $HOME/.ssh.
95 """
96- # XXX: Should this be managed via templates + contexts?
97- hosts = relation_get('known_hosts')
98- auth_keys = relation_get('authorized_keys')
99+ if prefix:
100+ hosts = relation_get('{}_known_hosts'.format(prefix))
101+ auth_keys = relation_get('{}_authorized_keys'.format(prefix))
102+ else:
103+ # XXX: Should this be managed via templates + contexts?
104+ hosts = relation_get('known_hosts')
105+ auth_keys = relation_get('authorized_keys')
106+
107 # XXX: Need to fix charm-helpers to return None for empty settings,
108 # in all cases.
109 if not hosts or not auth_keys:
110@@ -394,3 +399,13 @@
111 cmd = ['virsh', 'secret-set-value', '--secret', secret_uuid,
112 '--base64', key]
113 check_call(cmd)
114+
115+
116+def enable_shell(user):
117+ cmd = ['usermod', '-s', '/bin/bash', user]
118+ check_call(cmd)
119+
120+
121+def disable_shell(user):
122+ cmd = ['usermod', '-s', '/bin/false', user]
123+ check_call(cmd)
124
125=== modified file 'unit_tests/test_nova_compute_hooks.py'
126--- unit_tests/test_nova_compute_hooks.py 2013-11-17 21:47:29 +0000
127+++ unit_tests/test_nova_compute_hooks.py 2013-12-17 11:22:05 +0000
128@@ -48,6 +48,8 @@
129 'neutron_plugin',
130 'public_ssh_key',
131 'register_configs',
132+ 'disable_shell',
133+ 'enable_shell',
134 # misc_utils
135 'ensure_ceph_keyring',
136 'execd_preinstall'
137@@ -95,6 +97,38 @@
138 call('cloud-compute:1'),
139 ]
140 self.assertEquals(ex, compute_joined.call_args_list)
141+ self.assertTrue(self.initialize_ssh_keys.called)
142+
143+ @patch.object(hooks, 'compute_joined')
144+ def test_config_changed_with_resize(self, compute_joined):
145+ self.test_config.set('enable-resize', True)
146+ self.relation_ids.return_value = [
147+ 'cloud-compute:0',
148+ 'cloud-compute:1'
149+ ]
150+ hooks.config_changed()
151+ ex = [
152+ call('cloud-compute:0'),
153+ call('cloud-compute:1'),
154+ ]
155+ self.assertEquals(ex, compute_joined.call_args_list)
156+ self.initialize_ssh_keys.assert_called_with(user='nova')
157+ self.enable_shell.assert_called_with(user='nova')
158+
159+ @patch.object(hooks, 'compute_joined')
160+ def test_config_changed_without_resize(self, compute_joined):
161+ self.test_config.set('enable-resize', False)
162+ self.relation_ids.return_value = [
163+ 'cloud-compute:0',
164+ 'cloud-compute:1'
165+ ]
166+ hooks.config_changed()
167+ ex = [
168+ call('cloud-compute:0'),
169+ call('cloud-compute:1'),
170+ ]
171+ self.assertEquals(ex, compute_joined.call_args_list)
172+ self.disable_shell.assert_called_with(user='nova')
173
174 @patch.object(hooks, 'compute_joined')
175 def test_config_changed_no_upgrade_no_migration(self, compute_joined):
176@@ -239,7 +273,7 @@
177 hooks.image_service_changed()
178 configs.write.assert_called_with('/etc/nova/nova.conf')
179
180- def test_compute_joined_no_migration(self):
181+ def test_compute_joined_no_migration_no_resize(self):
182 self.migration_enabled.return_value = False
183 hooks.compute_joined()
184 self.assertFalse(self.relation_set.called)
185@@ -261,14 +295,27 @@
186 migration_auth_type='ssh'
187 )
188
189+ def test_compute_joined_with_resize(self):
190+ self.test_config.set('enable-resize', True)
191+ self.public_ssh_key.return_value = 'bar'
192+ hooks.compute_joined()
193+ self.relation_set.assert_called_with(
194+ relation_id=None,
195+ nova_ssh_public_key='bar'
196+ )
197+ hooks.compute_joined(rid='cloud-compute:2')
198+ self.relation_set.assert_called_with(
199+ relation_id='cloud-compute:2',
200+ nova_ssh_public_key='bar'
201+ )
202+
203 def test_compute_changed(self):
204 hooks.compute_changed()
205- expected_funcs = [
206- self.import_authorized_keys,
207- self.import_keystone_ca_cert,
208- ]
209- for func in expected_funcs:
210- self.assertTrue(func.called)
211+ self.assertTrue(self.import_keystone_ca_cert.called)
212+ self.import_authorized_keys.assert_has_calls([
213+ call(),
214+ call(user='nova', prefix='nova'),
215+ ])
216
217 def test_ceph_joined(self):
218 hooks.ceph_joined()
219
220=== modified file 'unit_tests/test_nova_compute_utils.py'
221--- unit_tests/test_nova_compute_utils.py 2013-11-17 21:47:29 +0000
222+++ unit_tests/test_nova_compute_utils.py 2013-12-17 11:22:05 +0000
223@@ -179,7 +179,7 @@
224 self.assertFalse(_open.called)
225
226 @patch('pwd.getpwnam')
227- def test_import_authorized_keys(self, getpwnam):
228+ def _test_import_authorized_keys_base(self, getpwnam, prefix=None):
229 getpwnam.return_value = self.fake_user('foo')
230 self.relation_get.side_effect = [
231 'Zm9vX2tleQo=', # relation_get('known_hosts')
232@@ -200,6 +200,38 @@
233 self.assertEquals(ex_open, _open.call_args_list)
234 self.assertEquals(ex_write, _file.write.call_args_list)
235
236+ self.relation_get.assert_has_called([
237+ call('known_hosts').
238+ call('authorized_keys')
239+ ])
240+
241+ @patch('pwd.getpwnam')
242+ def test_import_authorized_keys_prefix(self, getpwnam):
243+ getpwnam.return_value = self.fake_user('foo')
244+ self.relation_get.side_effect = [
245+ 'Zm9vX2tleQo=', # relation_get('known_hosts')
246+ 'Zm9vX2hvc3QK', # relation_get('authorized_keys')
247+ ]
248+
249+ ex_open = [
250+ call('/home/foo/.ssh/authorized_keys', 'wb'),
251+ call('/home/foo/.ssh/known_hosts', 'wb')
252+ ]
253+ ex_write = [
254+ call('foo_host\n'),
255+ call('foo_key\n'),
256+ ]
257+
258+ with patch_open() as (_open, _file):
259+ utils.import_authorized_keys(user='foo', prefix='bar')
260+ self.assertEquals(ex_open, _open.call_args_list)
261+ self.assertEquals(ex_write, _file.write.call_args_list)
262+
263+ self.relation_get.assert_has_called([
264+ call('bar_known_hosts').
265+ call('bar_authorized_keys')
266+ ])
267+
268 @patch('subprocess.check_call')
269 def test_import_keystone_cert_missing_data(self, check_call):
270 self.relation_get.return_value = None
271@@ -247,3 +279,14 @@
272 call('/etc/nova/nova.conf', [ctxt1])
273 ]
274 self.assertEquals(fake_renderer.register.call_args_list, ex_reg)
275+
276+ @patch.object(utils, 'check_call')
277+ def test_enable_shell(self, _check_call):
278+ utils.enable_shell('dummy')
279+ _check_call.assert_called_with(['usermod', '-s', '/bin/bash', 'dummy'])
280+
281+ @patch.object(utils, 'check_call')
282+ def test_disable_shell(self, _check_call):
283+ utils.disable_shell('dummy')
284+ _check_call.assert_called_with(['usermod', '-s', '/bin/false',
285+ 'dummy'])

Subscribers

People subscribed via source and target branches

to all changes: