Merge lp:~openstack-charmers/charms/precise/nova-compute/ods_merge into lp:~charmers/charms/precise/nova-compute/trunk
- Precise Pangolin (12.04)
- ods_merge
- Merge into trunk
Proposed by
Adam Gandelman
Status: | Merged |
---|---|
Merged at revision: | 50 |
Proposed branch: | lp:~openstack-charmers/charms/precise/nova-compute/ods_merge |
Merge into: | lp:~charmers/charms/precise/nova-compute/trunk |
Diff against target: |
1306 lines (+444/-83) 22 files modified
.project (+1/-1) .pydevproject (+2/-2) hooks/charmhelpers/contrib/network/ovs/__init__.py (+4/-1) hooks/charmhelpers/contrib/openstack/alternatives.py (+17/-0) hooks/charmhelpers/contrib/openstack/context.py (+20/-1) hooks/charmhelpers/contrib/openstack/neutron.py (+20/-0) hooks/charmhelpers/contrib/openstack/utils.py (+81/-10) hooks/charmhelpers/contrib/storage/linux/ceph.py (+27/-3) hooks/charmhelpers/core/hookenv.py (+78/-23) hooks/charmhelpers/core/host.py (+15/-9) hooks/charmhelpers/fetch/__init__.py (+53/-5) hooks/charmhelpers/fetch/bzrurl.py (+1/-1) hooks/nova_compute_context.py (+11/-0) hooks/nova_compute_hooks.py (+11/-8) hooks/nova_compute_utils.py (+35/-15) templates/essex/nova.conf (+3/-0) templates/folsom/nova.conf (+3/-0) templates/grizzly/nova.conf (+5/-0) templates/havana/nova.conf (+5/-0) unit_tests/test_nova_compute_contexts.py (+14/-0) unit_tests/test_nova_compute_hooks.py (+34/-4) unit_tests/test_nova_compute_utils.py (+4/-0) |
To merge this branch: | bzr merge lp:~openstack-charmers/charms/precise/nova-compute/ods_merge |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Marco Ceppi (community) | Abstain | ||
OpenStack Charmers | Pending | ||
Review via email: mp+194063@code.launchpad.net |
Commit message
Description of the change
* Adds Neutron NVP support
* Adds support for deploying alongside ceph via hulk smash
To post a comment you must log in.
- 59. By James Page
-
Rebase on trunk
Revision history for this message
Marco Ceppi (marcoceppi) : | # |
review:
Abstain
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.project' | |||
2 | --- .project 2013-09-20 16:51:57 +0000 | |||
3 | +++ .project 2013-11-08 05:49:21 +0000 | |||
4 | @@ -1,6 +1,6 @@ | |||
5 | 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
6 | 2 | <projectDescription> | 2 | <projectDescription> |
8 | 3 | <name>nova-compute</name> | 3 | <name>nvp-nova-compute</name> |
9 | 4 | <comment></comment> | 4 | <comment></comment> |
10 | 5 | <projects> | 5 | <projects> |
11 | 6 | </projects> | 6 | </projects> |
12 | 7 | 7 | ||
13 | === modified file '.pydevproject' | |||
14 | --- .pydevproject 2013-09-23 13:23:51 +0000 | |||
15 | +++ .pydevproject 2013-11-08 05:49:21 +0000 | |||
16 | @@ -3,7 +3,7 @@ | |||
17 | 3 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> | 3 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> |
18 | 4 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> | 4 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> |
19 | 5 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> | 5 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> |
22 | 6 | <path>/nova-compute/hooks</path> | 6 | <path>/nvp-nova-compute/hooks</path> |
23 | 7 | <path>/nova-compute/unit_tests</path> | 7 | <path>/nvp-nova-compute/unit_tests</path> |
24 | 8 | </pydev_pathproperty> | 8 | </pydev_pathproperty> |
25 | 9 | </pydev_project> | 9 | </pydev_project> |
26 | 10 | 10 | ||
27 | === modified file 'hooks/charmhelpers/contrib/network/ovs/__init__.py' | |||
28 | --- hooks/charmhelpers/contrib/network/ovs/__init__.py 2013-09-25 09:31:31 +0000 | |||
29 | +++ hooks/charmhelpers/contrib/network/ovs/__init__.py 2013-11-08 05:49:21 +0000 | |||
30 | @@ -69,4 +69,7 @@ | |||
31 | 69 | 69 | ||
32 | 70 | def full_restart(): | 70 | def full_restart(): |
33 | 71 | ''' Full restart and reload of openvswitch ''' | 71 | ''' Full restart and reload of openvswitch ''' |
35 | 72 | service('force-reload-kmod', 'openvswitch-switch') | 72 | if os.path.exists('/etc/init/openvswitch-force-reload-kmod.conf'): |
36 | 73 | service('start', 'openvswitch-force-reload-kmod') | ||
37 | 74 | else: | ||
38 | 75 | service('force-reload-kmod', 'openvswitch-switch') | ||
39 | 73 | 76 | ||
40 | === added file 'hooks/charmhelpers/contrib/openstack/alternatives.py' | |||
41 | --- hooks/charmhelpers/contrib/openstack/alternatives.py 1970-01-01 00:00:00 +0000 | |||
42 | +++ hooks/charmhelpers/contrib/openstack/alternatives.py 2013-11-08 05:49:21 +0000 | |||
43 | @@ -0,0 +1,17 @@ | |||
44 | 1 | ''' Helper for managing alternatives for file conflict resolution ''' | ||
45 | 2 | |||
46 | 3 | import subprocess | ||
47 | 4 | import shutil | ||
48 | 5 | import os | ||
49 | 6 | |||
50 | 7 | |||
51 | 8 | def install_alternative(name, target, source, priority=50): | ||
52 | 9 | ''' Install alternative configuration ''' | ||
53 | 10 | if (os.path.exists(target) and not os.path.islink(target)): | ||
54 | 11 | # Move existing file/directory away before installing | ||
55 | 12 | shutil.move(target, '{}.bak'.format(target)) | ||
56 | 13 | cmd = [ | ||
57 | 14 | 'update-alternatives', '--force', '--install', | ||
58 | 15 | target, name, source, str(priority) | ||
59 | 16 | ] | ||
60 | 17 | subprocess.check_call(cmd) | ||
61 | 0 | 18 | ||
62 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' | |||
63 | --- hooks/charmhelpers/contrib/openstack/context.py 2013-10-15 01:32:42 +0000 | |||
64 | +++ hooks/charmhelpers/contrib/openstack/context.py 2013-11-08 05:49:21 +0000 | |||
65 | @@ -385,16 +385,33 @@ | |||
66 | 385 | def ovs_ctxt(self): | 385 | def ovs_ctxt(self): |
67 | 386 | driver = neutron_plugin_attribute(self.plugin, 'driver', | 386 | driver = neutron_plugin_attribute(self.plugin, 'driver', |
68 | 387 | self.network_manager) | 387 | self.network_manager) |
70 | 388 | 388 | config = neutron_plugin_attribute(self.plugin, 'config', | |
71 | 389 | self.network_manager) | ||
72 | 389 | ovs_ctxt = { | 390 | ovs_ctxt = { |
73 | 390 | 'core_plugin': driver, | 391 | 'core_plugin': driver, |
74 | 391 | 'neutron_plugin': 'ovs', | 392 | 'neutron_plugin': 'ovs', |
75 | 392 | 'neutron_security_groups': self.neutron_security_groups, | 393 | 'neutron_security_groups': self.neutron_security_groups, |
76 | 393 | 'local_ip': unit_private_ip(), | 394 | 'local_ip': unit_private_ip(), |
77 | 395 | 'config': config | ||
78 | 394 | } | 396 | } |
79 | 395 | 397 | ||
80 | 396 | return ovs_ctxt | 398 | return ovs_ctxt |
81 | 397 | 399 | ||
82 | 400 | def nvp_ctxt(self): | ||
83 | 401 | driver = neutron_plugin_attribute(self.plugin, 'driver', | ||
84 | 402 | self.network_manager) | ||
85 | 403 | config = neutron_plugin_attribute(self.plugin, 'config', | ||
86 | 404 | self.network_manager) | ||
87 | 405 | nvp_ctxt = { | ||
88 | 406 | 'core_plugin': driver, | ||
89 | 407 | 'neutron_plugin': 'nvp', | ||
90 | 408 | 'neutron_security_groups': self.neutron_security_groups, | ||
91 | 409 | 'local_ip': unit_private_ip(), | ||
92 | 410 | 'config': config | ||
93 | 411 | } | ||
94 | 412 | |||
95 | 413 | return nvp_ctxt | ||
96 | 414 | |||
97 | 398 | def __call__(self): | 415 | def __call__(self): |
98 | 399 | self._ensure_packages() | 416 | self._ensure_packages() |
99 | 400 | 417 | ||
100 | @@ -408,6 +425,8 @@ | |||
101 | 408 | 425 | ||
102 | 409 | if self.plugin == 'ovs': | 426 | if self.plugin == 'ovs': |
103 | 410 | ctxt.update(self.ovs_ctxt()) | 427 | ctxt.update(self.ovs_ctxt()) |
104 | 428 | elif self.plugin == 'nvp': | ||
105 | 429 | ctxt.update(self.nvp_ctxt()) | ||
106 | 411 | 430 | ||
107 | 412 | self._save_flag_file() | 431 | self._save_flag_file() |
108 | 413 | return ctxt | 432 | return ctxt |
109 | 414 | 433 | ||
110 | === modified file 'hooks/charmhelpers/contrib/openstack/neutron.py' | |||
111 | --- hooks/charmhelpers/contrib/openstack/neutron.py 2013-10-15 01:32:42 +0000 | |||
112 | +++ hooks/charmhelpers/contrib/openstack/neutron.py 2013-11-08 05:49:21 +0000 | |||
113 | @@ -34,13 +34,23 @@ | |||
114 | 34 | 'services': ['quantum-plugin-openvswitch-agent'], | 34 | 'services': ['quantum-plugin-openvswitch-agent'], |
115 | 35 | 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], | 35 | 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], |
116 | 36 | ['quantum-plugin-openvswitch-agent']], | 36 | ['quantum-plugin-openvswitch-agent']], |
117 | 37 | 'server_packages': ['quantum-server', | ||
118 | 38 | 'quantum-plugin-openvswitch'], | ||
119 | 39 | 'server_services': ['quantum-server'] | ||
120 | 37 | }, | 40 | }, |
121 | 38 | 'nvp': { | 41 | 'nvp': { |
122 | 39 | 'config': '/etc/quantum/plugins/nicira/nvp.ini', | 42 | 'config': '/etc/quantum/plugins/nicira/nvp.ini', |
123 | 40 | 'driver': 'quantum.plugins.nicira.nicira_nvp_plugin.' | 43 | 'driver': 'quantum.plugins.nicira.nicira_nvp_plugin.' |
124 | 41 | 'QuantumPlugin.NvpPluginV2', | 44 | 'QuantumPlugin.NvpPluginV2', |
125 | 45 | 'contexts': [ | ||
126 | 46 | context.SharedDBContext(user=config('neutron-database-user'), | ||
127 | 47 | database=config('neutron-database'), | ||
128 | 48 | relation_prefix='neutron')], | ||
129 | 42 | 'services': [], | 49 | 'services': [], |
130 | 43 | 'packages': [], | 50 | 'packages': [], |
131 | 51 | 'server_packages': ['quantum-server', | ||
132 | 52 | 'quantum-plugin-nicira'], | ||
133 | 53 | 'server_services': ['quantum-server'] | ||
134 | 44 | } | 54 | } |
135 | 45 | } | 55 | } |
136 | 46 | 56 | ||
137 | @@ -60,13 +70,23 @@ | |||
138 | 60 | 'services': ['neutron-plugin-openvswitch-agent'], | 70 | 'services': ['neutron-plugin-openvswitch-agent'], |
139 | 61 | 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], | 71 | 'packages': [[headers_package(), 'openvswitch-datapath-dkms'], |
140 | 62 | ['quantum-plugin-openvswitch-agent']], | 72 | ['quantum-plugin-openvswitch-agent']], |
141 | 73 | 'server_packages': ['neutron-server', | ||
142 | 74 | 'neutron-plugin-openvswitch'], | ||
143 | 75 | 'server_services': ['neutron-server'] | ||
144 | 63 | }, | 76 | }, |
145 | 64 | 'nvp': { | 77 | 'nvp': { |
146 | 65 | 'config': '/etc/neutron/plugins/nicira/nvp.ini', | 78 | 'config': '/etc/neutron/plugins/nicira/nvp.ini', |
147 | 66 | 'driver': 'neutron.plugins.nicira.nicira_nvp_plugin.' | 79 | 'driver': 'neutron.plugins.nicira.nicira_nvp_plugin.' |
148 | 67 | 'NeutronPlugin.NvpPluginV2', | 80 | 'NeutronPlugin.NvpPluginV2', |
149 | 81 | 'contexts': [ | ||
150 | 82 | context.SharedDBContext(user=config('neutron-database-user'), | ||
151 | 83 | database=config('neutron-database'), | ||
152 | 84 | relation_prefix='neutron')], | ||
153 | 68 | 'services': [], | 85 | 'services': [], |
154 | 69 | 'packages': [], | 86 | 'packages': [], |
155 | 87 | 'server_packages': ['neutron-server', | ||
156 | 88 | 'neutron-plugin-nicira'], | ||
157 | 89 | 'server_services': ['neutron-server'] | ||
158 | 70 | } | 90 | } |
159 | 71 | } | 91 | } |
160 | 72 | 92 | ||
161 | 73 | 93 | ||
162 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' | |||
163 | --- hooks/charmhelpers/contrib/openstack/utils.py 2013-10-13 22:51:26 +0000 | |||
164 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2013-11-08 05:49:21 +0000 | |||
165 | @@ -13,19 +13,28 @@ | |||
166 | 13 | config, | 13 | config, |
167 | 14 | log as juju_log, | 14 | log as juju_log, |
168 | 15 | charm_dir, | 15 | charm_dir, |
178 | 16 | ) | 16 | ERROR, |
179 | 17 | 17 | INFO | |
180 | 18 | from charmhelpers.core.host import ( | 18 | ) |
181 | 19 | lsb_release, | 19 | |
182 | 20 | ) | 20 | from charmhelpers.contrib.storage.linux.lvm import ( |
183 | 21 | 21 | deactivate_lvm_volume_group, | |
184 | 22 | from charmhelpers.fetch import ( | 22 | is_lvm_physical_volume, |
185 | 23 | apt_install, | 23 | remove_lvm_physical_volume, |
186 | 24 | ) | 24 | ) |
187 | 25 | |||
188 | 26 | from charmhelpers.core.host import lsb_release, mounts, umount | ||
189 | 27 | from charmhelpers.fetch import apt_install | ||
190 | 28 | from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk | ||
191 | 29 | from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device | ||
192 | 25 | 30 | ||
193 | 26 | CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" | 31 | CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" |
194 | 27 | CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' | 32 | CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' |
195 | 28 | 33 | ||
196 | 34 | DISTRO_PROPOSED = ('deb http://archive.ubuntu.com/ubuntu/ %s-proposed ' | ||
197 | 35 | 'restricted main multiverse universe') | ||
198 | 36 | |||
199 | 37 | |||
200 | 29 | UBUNTU_OPENSTACK_RELEASE = OrderedDict([ | 38 | UBUNTU_OPENSTACK_RELEASE = OrderedDict([ |
201 | 30 | ('oneiric', 'diablo'), | 39 | ('oneiric', 'diablo'), |
202 | 31 | ('precise', 'essex'), | 40 | ('precise', 'essex'), |
203 | @@ -57,6 +66,8 @@ | |||
204 | 57 | ('1.9.0', 'havana'), | 66 | ('1.9.0', 'havana'), |
205 | 58 | ]) | 67 | ]) |
206 | 59 | 68 | ||
207 | 69 | DEFAULT_LOOPBACK_SIZE = '5G' | ||
208 | 70 | |||
209 | 60 | 71 | ||
210 | 61 | def error_out(msg): | 72 | def error_out(msg): |
211 | 62 | juju_log("FATAL ERROR: %s" % msg, level='ERROR') | 73 | juju_log("FATAL ERROR: %s" % msg, level='ERROR') |
212 | @@ -67,7 +78,7 @@ | |||
213 | 67 | '''Derive OpenStack release codename from a given installation source.''' | 78 | '''Derive OpenStack release codename from a given installation source.''' |
214 | 68 | ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] | 79 | ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
215 | 69 | rel = '' | 80 | rel = '' |
217 | 70 | if src == 'distro': | 81 | if src in ['distro', 'distro-proposed']: |
218 | 71 | try: | 82 | try: |
219 | 72 | rel = UBUNTU_OPENSTACK_RELEASE[ubuntu_rel] | 83 | rel = UBUNTU_OPENSTACK_RELEASE[ubuntu_rel] |
220 | 73 | except KeyError: | 84 | except KeyError: |
221 | @@ -202,6 +213,10 @@ | |||
222 | 202 | '''Configure apt installation source.''' | 213 | '''Configure apt installation source.''' |
223 | 203 | if rel == 'distro': | 214 | if rel == 'distro': |
224 | 204 | return | 215 | return |
225 | 216 | elif rel == 'distro-proposed': | ||
226 | 217 | ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] | ||
227 | 218 | with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f: | ||
228 | 219 | f.write(DISTRO_PROPOSED % ubuntu_rel) | ||
229 | 205 | elif rel[:4] == "ppa:": | 220 | elif rel[:4] == "ppa:": |
230 | 206 | src = rel | 221 | src = rel |
231 | 207 | subprocess.check_call(["add-apt-repository", "-y", src]) | 222 | subprocess.check_call(["add-apt-repository", "-y", src]) |
232 | @@ -299,6 +314,62 @@ | |||
233 | 299 | return apt.version_compare(available_vers, cur_vers) == 1 | 314 | return apt.version_compare(available_vers, cur_vers) == 1 |
234 | 300 | 315 | ||
235 | 301 | 316 | ||
236 | 317 | def ensure_block_device(block_device): | ||
237 | 318 | ''' | ||
238 | 319 | Confirm block_device, create as loopback if necessary. | ||
239 | 320 | |||
240 | 321 | :param block_device: str: Full path of block device to ensure. | ||
241 | 322 | |||
242 | 323 | :returns: str: Full path of ensured block device. | ||
243 | 324 | ''' | ||
244 | 325 | _none = ['None', 'none', None] | ||
245 | 326 | if (block_device in _none): | ||
246 | 327 | error_out('prepare_storage(): Missing required input: ' | ||
247 | 328 | 'block_device=%s.' % block_device, level=ERROR) | ||
248 | 329 | |||
249 | 330 | if block_device.startswith('/dev/'): | ||
250 | 331 | bdev = block_device | ||
251 | 332 | elif block_device.startswith('/'): | ||
252 | 333 | _bd = block_device.split('|') | ||
253 | 334 | if len(_bd) == 2: | ||
254 | 335 | bdev, size = _bd | ||
255 | 336 | else: | ||
256 | 337 | bdev = block_device | ||
257 | 338 | size = DEFAULT_LOOPBACK_SIZE | ||
258 | 339 | bdev = ensure_loopback_device(bdev, size) | ||
259 | 340 | else: | ||
260 | 341 | bdev = '/dev/%s' % block_device | ||
261 | 342 | |||
262 | 343 | if not is_block_device(bdev): | ||
263 | 344 | error_out('Failed to locate valid block device at %s' % bdev, | ||
264 | 345 | level=ERROR) | ||
265 | 346 | |||
266 | 347 | return bdev | ||
267 | 348 | |||
268 | 349 | |||
269 | 350 | def clean_storage(block_device): | ||
270 | 351 | ''' | ||
271 | 352 | Ensures a block device is clean. That is: | ||
272 | 353 | - unmounted | ||
273 | 354 | - any lvm volume groups are deactivated | ||
274 | 355 | - any lvm physical device signatures removed | ||
275 | 356 | - partition table wiped | ||
276 | 357 | |||
277 | 358 | :param block_device: str: Full path to block device to clean. | ||
278 | 359 | ''' | ||
279 | 360 | for mp, d in mounts(): | ||
280 | 361 | if d == block_device: | ||
281 | 362 | juju_log('clean_storage(): %s is mounted @ %s, unmounting.' % | ||
282 | 363 | (d, mp), level=INFO) | ||
283 | 364 | umount(mp, persist=True) | ||
284 | 365 | |||
285 | 366 | if is_lvm_physical_volume(block_device): | ||
286 | 367 | deactivate_lvm_volume_group(block_device) | ||
287 | 368 | remove_lvm_physical_volume(block_device) | ||
288 | 369 | else: | ||
289 | 370 | zap_disk(block_device) | ||
290 | 371 | |||
291 | 372 | |||
292 | 302 | def is_ip(address): | 373 | def is_ip(address): |
293 | 303 | """ | 374 | """ |
294 | 304 | Returns True if address is a valid IP address. | 375 | Returns True if address is a valid IP address. |
295 | 305 | 376 | ||
296 | === modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py' | |||
297 | --- hooks/charmhelpers/contrib/storage/linux/ceph.py 2013-09-25 09:31:31 +0000 | |||
298 | +++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2013-11-08 05:49:21 +0000 | |||
299 | @@ -102,8 +102,12 @@ | |||
300 | 102 | Return a list of all Ceph Object Storage Daemons | 102 | Return a list of all Ceph Object Storage Daemons |
301 | 103 | currently in the cluster | 103 | currently in the cluster |
302 | 104 | ''' | 104 | ''' |
305 | 105 | return json.loads(check_output(['ceph', '--id', service, | 105 | version = ceph_version() |
306 | 106 | 'osd', 'ls', '--format=json'])) | 106 | if version and version >= '0.56': |
307 | 107 | return json.loads(check_output(['ceph', '--id', service, | ||
308 | 108 | 'osd', 'ls', '--format=json'])) | ||
309 | 109 | else: | ||
310 | 110 | return None | ||
311 | 107 | 111 | ||
312 | 108 | 112 | ||
313 | 109 | def create_pool(service, name, replicas=2): | 113 | def create_pool(service, name, replicas=2): |
314 | @@ -114,7 +118,13 @@ | |||
315 | 114 | return | 118 | return |
316 | 115 | # Calculate the number of placement groups based | 119 | # Calculate the number of placement groups based |
317 | 116 | # on upstream recommended best practices. | 120 | # on upstream recommended best practices. |
319 | 117 | pgnum = (len(get_osds(service)) * 100 / replicas) | 121 | osds = get_osds(service) |
320 | 122 | if osds: | ||
321 | 123 | pgnum = (len(osds) * 100 / replicas) | ||
322 | 124 | else: | ||
323 | 125 | # NOTE(james-page): Default to 200 for older ceph versions | ||
324 | 126 | # which don't support OSD query from cli | ||
325 | 127 | pgnum = 200 | ||
326 | 118 | cmd = [ | 128 | cmd = [ |
327 | 119 | 'ceph', '--id', service, | 129 | 'ceph', '--id', service, |
328 | 120 | 'osd', 'pool', 'create', | 130 | 'osd', 'pool', 'create', |
329 | @@ -357,3 +367,17 @@ | |||
330 | 357 | if user and group: | 367 | if user and group: |
331 | 358 | check_call(['chown', '%s.%s' % (user, group), keyring]) | 368 | check_call(['chown', '%s.%s' % (user, group), keyring]) |
332 | 359 | return True | 369 | return True |
333 | 370 | |||
334 | 371 | |||
335 | 372 | def ceph_version(): | ||
336 | 373 | ''' Retrieve the local version of ceph ''' | ||
337 | 374 | if os.path.exists('/usr/bin/ceph'): | ||
338 | 375 | cmd = ['ceph', '-v'] | ||
339 | 376 | output = check_output(cmd) | ||
340 | 377 | output = output.split() | ||
341 | 378 | if len(output) > 3: | ||
342 | 379 | return output[2] | ||
343 | 380 | else: | ||
344 | 381 | return None | ||
345 | 382 | else: | ||
346 | 383 | return None | ||
347 | 360 | 384 | ||
348 | === modified file 'hooks/charmhelpers/core/hookenv.py' | |||
349 | --- hooks/charmhelpers/core/hookenv.py 2013-07-19 02:37:30 +0000 | |||
350 | +++ hooks/charmhelpers/core/hookenv.py 2013-11-08 05:49:21 +0000 | |||
351 | @@ -9,6 +9,7 @@ | |||
352 | 9 | import yaml | 9 | import yaml |
353 | 10 | import subprocess | 10 | import subprocess |
354 | 11 | import UserDict | 11 | import UserDict |
355 | 12 | from subprocess import CalledProcessError | ||
356 | 12 | 13 | ||
357 | 13 | CRITICAL = "CRITICAL" | 14 | CRITICAL = "CRITICAL" |
358 | 14 | ERROR = "ERROR" | 15 | ERROR = "ERROR" |
359 | @@ -21,7 +22,7 @@ | |||
360 | 21 | 22 | ||
361 | 22 | 23 | ||
362 | 23 | def cached(func): | 24 | def cached(func): |
364 | 24 | ''' Cache return values for multiple executions of func + args | 25 | """Cache return values for multiple executions of func + args |
365 | 25 | 26 | ||
366 | 26 | For example: | 27 | For example: |
367 | 27 | 28 | ||
368 | @@ -32,7 +33,7 @@ | |||
369 | 32 | unit_get('test') | 33 | unit_get('test') |
370 | 33 | 34 | ||
371 | 34 | will cache the result of unit_get + 'test' for future calls. | 35 | will cache the result of unit_get + 'test' for future calls. |
373 | 35 | ''' | 36 | """ |
374 | 36 | def wrapper(*args, **kwargs): | 37 | def wrapper(*args, **kwargs): |
375 | 37 | global cache | 38 | global cache |
376 | 38 | key = str((func, args, kwargs)) | 39 | key = str((func, args, kwargs)) |
377 | @@ -46,8 +47,8 @@ | |||
378 | 46 | 47 | ||
379 | 47 | 48 | ||
380 | 48 | def flush(key): | 49 | def flush(key): |
383 | 49 | ''' Flushes any entries from function cache where the | 50 | """Flushes any entries from function cache where the |
384 | 50 | key is found in the function+args ''' | 51 | key is found in the function+args """ |
385 | 51 | flush_list = [] | 52 | flush_list = [] |
386 | 52 | for item in cache: | 53 | for item in cache: |
387 | 53 | if key in item: | 54 | if key in item: |
388 | @@ -57,7 +58,7 @@ | |||
389 | 57 | 58 | ||
390 | 58 | 59 | ||
391 | 59 | def log(message, level=None): | 60 | def log(message, level=None): |
393 | 60 | "Write a message to the juju log" | 61 | """Write a message to the juju log""" |
394 | 61 | command = ['juju-log'] | 62 | command = ['juju-log'] |
395 | 62 | if level: | 63 | if level: |
396 | 63 | command += ['-l', level] | 64 | command += ['-l', level] |
397 | @@ -66,7 +67,7 @@ | |||
398 | 66 | 67 | ||
399 | 67 | 68 | ||
400 | 68 | class Serializable(UserDict.IterableUserDict): | 69 | class Serializable(UserDict.IterableUserDict): |
402 | 69 | "Wrapper, an object that can be serialized to yaml or json" | 70 | """Wrapper, an object that can be serialized to yaml or json""" |
403 | 70 | 71 | ||
404 | 71 | def __init__(self, obj): | 72 | def __init__(self, obj): |
405 | 72 | # wrap the object | 73 | # wrap the object |
406 | @@ -96,11 +97,11 @@ | |||
407 | 96 | self.data = state | 97 | self.data = state |
408 | 97 | 98 | ||
409 | 98 | def json(self): | 99 | def json(self): |
411 | 99 | "Serialize the object to json" | 100 | """Serialize the object to json""" |
412 | 100 | return json.dumps(self.data) | 101 | return json.dumps(self.data) |
413 | 101 | 102 | ||
414 | 102 | def yaml(self): | 103 | def yaml(self): |
416 | 103 | "Serialize the object to yaml" | 104 | """Serialize the object to yaml""" |
417 | 104 | return yaml.dump(self.data) | 105 | return yaml.dump(self.data) |
418 | 105 | 106 | ||
419 | 106 | 107 | ||
420 | @@ -119,38 +120,38 @@ | |||
421 | 119 | 120 | ||
422 | 120 | 121 | ||
423 | 121 | def in_relation_hook(): | 122 | def in_relation_hook(): |
425 | 122 | "Determine whether we're running in a relation hook" | 123 | """Determine whether we're running in a relation hook""" |
426 | 123 | return 'JUJU_RELATION' in os.environ | 124 | return 'JUJU_RELATION' in os.environ |
427 | 124 | 125 | ||
428 | 125 | 126 | ||
429 | 126 | def relation_type(): | 127 | def relation_type(): |
431 | 127 | "The scope for the current relation hook" | 128 | """The scope for the current relation hook""" |
432 | 128 | return os.environ.get('JUJU_RELATION', None) | 129 | return os.environ.get('JUJU_RELATION', None) |
433 | 129 | 130 | ||
434 | 130 | 131 | ||
435 | 131 | def relation_id(): | 132 | def relation_id(): |
437 | 132 | "The relation ID for the current relation hook" | 133 | """The relation ID for the current relation hook""" |
438 | 133 | return os.environ.get('JUJU_RELATION_ID', None) | 134 | return os.environ.get('JUJU_RELATION_ID', None) |
439 | 134 | 135 | ||
440 | 135 | 136 | ||
441 | 136 | def local_unit(): | 137 | def local_unit(): |
443 | 137 | "Local unit ID" | 138 | """Local unit ID""" |
444 | 138 | return os.environ['JUJU_UNIT_NAME'] | 139 | return os.environ['JUJU_UNIT_NAME'] |
445 | 139 | 140 | ||
446 | 140 | 141 | ||
447 | 141 | def remote_unit(): | 142 | def remote_unit(): |
449 | 142 | "The remote unit for the current relation hook" | 143 | """The remote unit for the current relation hook""" |
450 | 143 | return os.environ['JUJU_REMOTE_UNIT'] | 144 | return os.environ['JUJU_REMOTE_UNIT'] |
451 | 144 | 145 | ||
452 | 145 | 146 | ||
453 | 146 | def service_name(): | 147 | def service_name(): |
455 | 147 | "The name service group this unit belongs to" | 148 | """The name service group this unit belongs to""" |
456 | 148 | return local_unit().split('/')[0] | 149 | return local_unit().split('/')[0] |
457 | 149 | 150 | ||
458 | 150 | 151 | ||
459 | 151 | @cached | 152 | @cached |
460 | 152 | def config(scope=None): | 153 | def config(scope=None): |
462 | 153 | "Juju charm configuration" | 154 | """Juju charm configuration""" |
463 | 154 | config_cmd_line = ['config-get'] | 155 | config_cmd_line = ['config-get'] |
464 | 155 | if scope is not None: | 156 | if scope is not None: |
465 | 156 | config_cmd_line.append(scope) | 157 | config_cmd_line.append(scope) |
466 | @@ -163,6 +164,7 @@ | |||
467 | 163 | 164 | ||
468 | 164 | @cached | 165 | @cached |
469 | 165 | def relation_get(attribute=None, unit=None, rid=None): | 166 | def relation_get(attribute=None, unit=None, rid=None): |
470 | 167 | """Get relation information""" | ||
471 | 166 | _args = ['relation-get', '--format=json'] | 168 | _args = ['relation-get', '--format=json'] |
472 | 167 | if rid: | 169 | if rid: |
473 | 168 | _args.append('-r') | 170 | _args.append('-r') |
474 | @@ -174,9 +176,14 @@ | |||
475 | 174 | return json.loads(subprocess.check_output(_args)) | 176 | return json.loads(subprocess.check_output(_args)) |
476 | 175 | except ValueError: | 177 | except ValueError: |
477 | 176 | return None | 178 | return None |
478 | 179 | except CalledProcessError, e: | ||
479 | 180 | if e.returncode == 2: | ||
480 | 181 | return None | ||
481 | 182 | raise | ||
482 | 177 | 183 | ||
483 | 178 | 184 | ||
484 | 179 | def relation_set(relation_id=None, relation_settings={}, **kwargs): | 185 | def relation_set(relation_id=None, relation_settings={}, **kwargs): |
485 | 186 | """Set relation information for the current unit""" | ||
486 | 180 | relation_cmd_line = ['relation-set'] | 187 | relation_cmd_line = ['relation-set'] |
487 | 181 | if relation_id is not None: | 188 | if relation_id is not None: |
488 | 182 | relation_cmd_line.extend(('-r', relation_id)) | 189 | relation_cmd_line.extend(('-r', relation_id)) |
489 | @@ -192,7 +199,7 @@ | |||
490 | 192 | 199 | ||
491 | 193 | @cached | 200 | @cached |
492 | 194 | def relation_ids(reltype=None): | 201 | def relation_ids(reltype=None): |
494 | 195 | "A list of relation_ids" | 202 | """A list of relation_ids""" |
495 | 196 | reltype = reltype or relation_type() | 203 | reltype = reltype or relation_type() |
496 | 197 | relid_cmd_line = ['relation-ids', '--format=json'] | 204 | relid_cmd_line = ['relation-ids', '--format=json'] |
497 | 198 | if reltype is not None: | 205 | if reltype is not None: |
498 | @@ -203,7 +210,7 @@ | |||
499 | 203 | 210 | ||
500 | 204 | @cached | 211 | @cached |
501 | 205 | def related_units(relid=None): | 212 | def related_units(relid=None): |
503 | 206 | "A list of related units" | 213 | """A list of related units""" |
504 | 207 | relid = relid or relation_id() | 214 | relid = relid or relation_id() |
505 | 208 | units_cmd_line = ['relation-list', '--format=json'] | 215 | units_cmd_line = ['relation-list', '--format=json'] |
506 | 209 | if relid is not None: | 216 | if relid is not None: |
507 | @@ -213,7 +220,7 @@ | |||
508 | 213 | 220 | ||
509 | 214 | @cached | 221 | @cached |
510 | 215 | def relation_for_unit(unit=None, rid=None): | 222 | def relation_for_unit(unit=None, rid=None): |
512 | 216 | "Get the json represenation of a unit's relation" | 223 | """Get the json represenation of a unit's relation""" |
513 | 217 | unit = unit or remote_unit() | 224 | unit = unit or remote_unit() |
514 | 218 | relation = relation_get(unit=unit, rid=rid) | 225 | relation = relation_get(unit=unit, rid=rid) |
515 | 219 | for key in relation: | 226 | for key in relation: |
516 | @@ -225,7 +232,7 @@ | |||
517 | 225 | 232 | ||
518 | 226 | @cached | 233 | @cached |
519 | 227 | def relations_for_id(relid=None): | 234 | def relations_for_id(relid=None): |
521 | 228 | "Get relations of a specific relation ID" | 235 | """Get relations of a specific relation ID""" |
522 | 229 | relation_data = [] | 236 | relation_data = [] |
523 | 230 | relid = relid or relation_ids() | 237 | relid = relid or relation_ids() |
524 | 231 | for unit in related_units(relid): | 238 | for unit in related_units(relid): |
525 | @@ -237,7 +244,7 @@ | |||
526 | 237 | 244 | ||
527 | 238 | @cached | 245 | @cached |
528 | 239 | def relations_of_type(reltype=None): | 246 | def relations_of_type(reltype=None): |
530 | 240 | "Get relations of a specific type" | 247 | """Get relations of a specific type""" |
531 | 241 | relation_data = [] | 248 | relation_data = [] |
532 | 242 | reltype = reltype or relation_type() | 249 | reltype = reltype or relation_type() |
533 | 243 | for relid in relation_ids(reltype): | 250 | for relid in relation_ids(reltype): |
534 | @@ -249,7 +256,7 @@ | |||
535 | 249 | 256 | ||
536 | 250 | @cached | 257 | @cached |
537 | 251 | def relation_types(): | 258 | def relation_types(): |
539 | 252 | "Get a list of relation types supported by this charm" | 259 | """Get a list of relation types supported by this charm""" |
540 | 253 | charmdir = os.environ.get('CHARM_DIR', '') | 260 | charmdir = os.environ.get('CHARM_DIR', '') |
541 | 254 | mdf = open(os.path.join(charmdir, 'metadata.yaml')) | 261 | mdf = open(os.path.join(charmdir, 'metadata.yaml')) |
542 | 255 | md = yaml.safe_load(mdf) | 262 | md = yaml.safe_load(mdf) |
543 | @@ -264,6 +271,7 @@ | |||
544 | 264 | 271 | ||
545 | 265 | @cached | 272 | @cached |
546 | 266 | def relations(): | 273 | def relations(): |
547 | 274 | """Get a nested dictionary of relation data for all related units""" | ||
548 | 267 | rels = {} | 275 | rels = {} |
549 | 268 | for reltype in relation_types(): | 276 | for reltype in relation_types(): |
550 | 269 | relids = {} | 277 | relids = {} |
551 | @@ -277,15 +285,35 @@ | |||
552 | 277 | return rels | 285 | return rels |
553 | 278 | 286 | ||
554 | 279 | 287 | ||
555 | 288 | @cached | ||
556 | 289 | def is_relation_made(relation, keys='private-address'): | ||
557 | 290 | ''' | ||
558 | 291 | Determine whether a relation is established by checking for | ||
559 | 292 | presence of key(s). If a list of keys is provided, they | ||
560 | 293 | must all be present for the relation to be identified as made | ||
561 | 294 | ''' | ||
562 | 295 | if isinstance(keys, str): | ||
563 | 296 | keys = [keys] | ||
564 | 297 | for r_id in relation_ids(relation): | ||
565 | 298 | for unit in related_units(r_id): | ||
566 | 299 | context = {} | ||
567 | 300 | for k in keys: | ||
568 | 301 | context[k] = relation_get(k, rid=r_id, | ||
569 | 302 | unit=unit) | ||
570 | 303 | if None not in context.values(): | ||
571 | 304 | return True | ||
572 | 305 | return False | ||
573 | 306 | |||
574 | 307 | |||
575 | 280 | def open_port(port, protocol="TCP"): | 308 | def open_port(port, protocol="TCP"): |
577 | 281 | "Open a service network port" | 309 | """Open a service network port""" |
578 | 282 | _args = ['open-port'] | 310 | _args = ['open-port'] |
579 | 283 | _args.append('{}/{}'.format(port, protocol)) | 311 | _args.append('{}/{}'.format(port, protocol)) |
580 | 284 | subprocess.check_call(_args) | 312 | subprocess.check_call(_args) |
581 | 285 | 313 | ||
582 | 286 | 314 | ||
583 | 287 | def close_port(port, protocol="TCP"): | 315 | def close_port(port, protocol="TCP"): |
585 | 288 | "Close a service network port" | 316 | """Close a service network port""" |
586 | 289 | _args = ['close-port'] | 317 | _args = ['close-port'] |
587 | 290 | _args.append('{}/{}'.format(port, protocol)) | 318 | _args.append('{}/{}'.format(port, protocol)) |
588 | 291 | subprocess.check_call(_args) | 319 | subprocess.check_call(_args) |
589 | @@ -293,6 +321,7 @@ | |||
590 | 293 | 321 | ||
591 | 294 | @cached | 322 | @cached |
592 | 295 | def unit_get(attribute): | 323 | def unit_get(attribute): |
593 | 324 | """Get the unit ID for the remote unit""" | ||
594 | 296 | _args = ['unit-get', '--format=json', attribute] | 325 | _args = ['unit-get', '--format=json', attribute] |
595 | 297 | try: | 326 | try: |
596 | 298 | return json.loads(subprocess.check_output(_args)) | 327 | return json.loads(subprocess.check_output(_args)) |
597 | @@ -301,22 +330,46 @@ | |||
598 | 301 | 330 | ||
599 | 302 | 331 | ||
600 | 303 | def unit_private_ip(): | 332 | def unit_private_ip(): |
601 | 333 | """Get this unit's private IP address""" | ||
602 | 304 | return unit_get('private-address') | 334 | return unit_get('private-address') |
603 | 305 | 335 | ||
604 | 306 | 336 | ||
605 | 307 | class UnregisteredHookError(Exception): | 337 | class UnregisteredHookError(Exception): |
606 | 338 | """Raised when an undefined hook is called""" | ||
607 | 308 | pass | 339 | pass |
608 | 309 | 340 | ||
609 | 310 | 341 | ||
610 | 311 | class Hooks(object): | 342 | class Hooks(object): |
611 | 343 | """A convenient handler for hook functions. | ||
612 | 344 | |||
613 | 345 | Example: | ||
614 | 346 | hooks = Hooks() | ||
615 | 347 | |||
616 | 348 | # register a hook, taking its name from the function name | ||
617 | 349 | @hooks.hook() | ||
618 | 350 | def install(): | ||
619 | 351 | ... | ||
620 | 352 | |||
621 | 353 | # register a hook, providing a custom hook name | ||
622 | 354 | @hooks.hook("config-changed") | ||
623 | 355 | def config_changed(): | ||
624 | 356 | ... | ||
625 | 357 | |||
626 | 358 | if __name__ == "__main__": | ||
627 | 359 | # execute a hook based on the name the program is called by | ||
628 | 360 | hooks.execute(sys.argv) | ||
629 | 361 | """ | ||
630 | 362 | |||
631 | 312 | def __init__(self): | 363 | def __init__(self): |
632 | 313 | super(Hooks, self).__init__() | 364 | super(Hooks, self).__init__() |
633 | 314 | self._hooks = {} | 365 | self._hooks = {} |
634 | 315 | 366 | ||
635 | 316 | def register(self, name, function): | 367 | def register(self, name, function): |
636 | 368 | """Register a hook""" | ||
637 | 317 | self._hooks[name] = function | 369 | self._hooks[name] = function |
638 | 318 | 370 | ||
639 | 319 | def execute(self, args): | 371 | def execute(self, args): |
640 | 372 | """Execute a registered hook based on args[0]""" | ||
641 | 320 | hook_name = os.path.basename(args[0]) | 373 | hook_name = os.path.basename(args[0]) |
642 | 321 | if hook_name in self._hooks: | 374 | if hook_name in self._hooks: |
643 | 322 | self._hooks[hook_name]() | 375 | self._hooks[hook_name]() |
644 | @@ -324,6 +377,7 @@ | |||
645 | 324 | raise UnregisteredHookError(hook_name) | 377 | raise UnregisteredHookError(hook_name) |
646 | 325 | 378 | ||
647 | 326 | def hook(self, *hook_names): | 379 | def hook(self, *hook_names): |
648 | 380 | """Decorator, registering them as hooks""" | ||
649 | 327 | def wrapper(decorated): | 381 | def wrapper(decorated): |
650 | 328 | for hook_name in hook_names: | 382 | for hook_name in hook_names: |
651 | 329 | self.register(hook_name, decorated) | 383 | self.register(hook_name, decorated) |
652 | @@ -337,4 +391,5 @@ | |||
653 | 337 | 391 | ||
654 | 338 | 392 | ||
655 | 339 | def charm_dir(): | 393 | def charm_dir(): |
656 | 394 | """Return the root directory of the current charm""" | ||
657 | 340 | return os.environ.get('CHARM_DIR') | 395 | return os.environ.get('CHARM_DIR') |
658 | 341 | 396 | ||
659 | === modified file 'hooks/charmhelpers/core/host.py' | |||
660 | --- hooks/charmhelpers/core/host.py 2013-09-20 16:40:54 +0000 | |||
661 | +++ hooks/charmhelpers/core/host.py 2013-11-08 05:49:21 +0000 | |||
662 | @@ -19,18 +19,22 @@ | |||
663 | 19 | 19 | ||
664 | 20 | 20 | ||
665 | 21 | def service_start(service_name): | 21 | def service_start(service_name): |
666 | 22 | """Start a system service""" | ||
667 | 22 | return service('start', service_name) | 23 | return service('start', service_name) |
668 | 23 | 24 | ||
669 | 24 | 25 | ||
670 | 25 | def service_stop(service_name): | 26 | def service_stop(service_name): |
671 | 27 | """Stop a system service""" | ||
672 | 26 | return service('stop', service_name) | 28 | return service('stop', service_name) |
673 | 27 | 29 | ||
674 | 28 | 30 | ||
675 | 29 | def service_restart(service_name): | 31 | def service_restart(service_name): |
676 | 32 | """Restart a system service""" | ||
677 | 30 | return service('restart', service_name) | 33 | return service('restart', service_name) |
678 | 31 | 34 | ||
679 | 32 | 35 | ||
680 | 33 | def service_reload(service_name, restart_on_failure=False): | 36 | def service_reload(service_name, restart_on_failure=False): |
681 | 37 | """Reload a system service, optionally falling back to restart if reload fails""" | ||
682 | 34 | service_result = service('reload', service_name) | 38 | service_result = service('reload', service_name) |
683 | 35 | if not service_result and restart_on_failure: | 39 | if not service_result and restart_on_failure: |
684 | 36 | service_result = service('restart', service_name) | 40 | service_result = service('restart', service_name) |
685 | @@ -38,11 +42,13 @@ | |||
686 | 38 | 42 | ||
687 | 39 | 43 | ||
688 | 40 | def service(action, service_name): | 44 | def service(action, service_name): |
689 | 45 | """Control a system service""" | ||
690 | 41 | cmd = ['service', service_name, action] | 46 | cmd = ['service', service_name, action] |
691 | 42 | return subprocess.call(cmd) == 0 | 47 | return subprocess.call(cmd) == 0 |
692 | 43 | 48 | ||
693 | 44 | 49 | ||
694 | 45 | def service_running(service): | 50 | def service_running(service): |
695 | 51 | """Determine whether a system service is running""" | ||
696 | 46 | try: | 52 | try: |
697 | 47 | output = subprocess.check_output(['service', service, 'status']) | 53 | output = subprocess.check_output(['service', service, 'status']) |
698 | 48 | except subprocess.CalledProcessError: | 54 | except subprocess.CalledProcessError: |
699 | @@ -55,7 +61,7 @@ | |||
700 | 55 | 61 | ||
701 | 56 | 62 | ||
702 | 57 | def adduser(username, password=None, shell='/bin/bash', system_user=False): | 63 | def adduser(username, password=None, shell='/bin/bash', system_user=False): |
704 | 58 | """Add a user""" | 64 | """Add a user to the system""" |
705 | 59 | try: | 65 | try: |
706 | 60 | user_info = pwd.getpwnam(username) | 66 | user_info = pwd.getpwnam(username) |
707 | 61 | log('user {0} already exists!'.format(username)) | 67 | log('user {0} already exists!'.format(username)) |
708 | @@ -138,7 +144,7 @@ | |||
709 | 138 | 144 | ||
710 | 139 | 145 | ||
711 | 140 | def mount(device, mountpoint, options=None, persist=False): | 146 | def mount(device, mountpoint, options=None, persist=False): |
713 | 141 | '''Mount a filesystem''' | 147 | """Mount a filesystem at a particular mountpoint""" |
714 | 142 | cmd_args = ['mount'] | 148 | cmd_args = ['mount'] |
715 | 143 | if options is not None: | 149 | if options is not None: |
716 | 144 | cmd_args.extend(['-o', options]) | 150 | cmd_args.extend(['-o', options]) |
717 | @@ -155,7 +161,7 @@ | |||
718 | 155 | 161 | ||
719 | 156 | 162 | ||
720 | 157 | def umount(mountpoint, persist=False): | 163 | def umount(mountpoint, persist=False): |
722 | 158 | '''Unmount a filesystem''' | 164 | """Unmount a filesystem""" |
723 | 159 | cmd_args = ['umount', mountpoint] | 165 | cmd_args = ['umount', mountpoint] |
724 | 160 | try: | 166 | try: |
725 | 161 | subprocess.check_output(cmd_args) | 167 | subprocess.check_output(cmd_args) |
726 | @@ -169,7 +175,7 @@ | |||
727 | 169 | 175 | ||
728 | 170 | 176 | ||
729 | 171 | def mounts(): | 177 | def mounts(): |
731 | 172 | '''List of all mounted volumes as [[mountpoint,device],[...]]''' | 178 | """Get a list of all mounted volumes as [[mountpoint,device],[...]]""" |
732 | 173 | with open('/proc/mounts') as f: | 179 | with open('/proc/mounts') as f: |
733 | 174 | # [['/mount/point','/dev/path'],[...]] | 180 | # [['/mount/point','/dev/path'],[...]] |
734 | 175 | system_mounts = [m[1::-1] for m in [l.strip().split() | 181 | system_mounts = [m[1::-1] for m in [l.strip().split() |
735 | @@ -178,7 +184,7 @@ | |||
736 | 178 | 184 | ||
737 | 179 | 185 | ||
738 | 180 | def file_hash(path): | 186 | def file_hash(path): |
740 | 181 | ''' Generate a md5 hash of the contents of 'path' or None if not found ''' | 187 | """Generate a md5 hash of the contents of 'path' or None if not found """ |
741 | 182 | if os.path.exists(path): | 188 | if os.path.exists(path): |
742 | 183 | h = hashlib.md5() | 189 | h = hashlib.md5() |
743 | 184 | with open(path, 'r') as source: | 190 | with open(path, 'r') as source: |
744 | @@ -189,7 +195,7 @@ | |||
745 | 189 | 195 | ||
746 | 190 | 196 | ||
747 | 191 | def restart_on_change(restart_map): | 197 | def restart_on_change(restart_map): |
749 | 192 | ''' Restart services based on configuration files changing | 198 | """Restart services based on configuration files changing |
750 | 193 | 199 | ||
751 | 194 | This function is used a decorator, for example | 200 | This function is used a decorator, for example |
752 | 195 | 201 | ||
753 | @@ -202,7 +208,7 @@ | |||
754 | 202 | In this example, the cinder-api and cinder-volume services | 208 | In this example, the cinder-api and cinder-volume services |
755 | 203 | would be restarted if /etc/ceph/ceph.conf is changed by the | 209 | would be restarted if /etc/ceph/ceph.conf is changed by the |
756 | 204 | ceph_client_changed function. | 210 | ceph_client_changed function. |
758 | 205 | ''' | 211 | """ |
759 | 206 | def wrap(f): | 212 | def wrap(f): |
760 | 207 | def wrapped_f(*args): | 213 | def wrapped_f(*args): |
761 | 208 | checksums = {} | 214 | checksums = {} |
762 | @@ -220,7 +226,7 @@ | |||
763 | 220 | 226 | ||
764 | 221 | 227 | ||
765 | 222 | def lsb_release(): | 228 | def lsb_release(): |
767 | 223 | '''Return /etc/lsb-release in a dict''' | 229 | """Return /etc/lsb-release in a dict""" |
768 | 224 | d = {} | 230 | d = {} |
769 | 225 | with open('/etc/lsb-release', 'r') as lsb: | 231 | with open('/etc/lsb-release', 'r') as lsb: |
770 | 226 | for l in lsb: | 232 | for l in lsb: |
771 | @@ -230,7 +236,7 @@ | |||
772 | 230 | 236 | ||
773 | 231 | 237 | ||
774 | 232 | def pwgen(length=None): | 238 | def pwgen(length=None): |
776 | 233 | '''Generate a random pasword.''' | 239 | """Generate a random pasword.""" |
777 | 234 | if length is None: | 240 | if length is None: |
778 | 235 | length = random.choice(range(35, 45)) | 241 | length = random.choice(range(35, 45)) |
779 | 236 | alphanumeric_chars = [ | 242 | alphanumeric_chars = [ |
780 | 237 | 243 | ||
781 | === modified file 'hooks/charmhelpers/fetch/__init__.py' | |||
782 | --- hooks/charmhelpers/fetch/__init__.py 2013-09-23 13:23:51 +0000 | |||
783 | +++ hooks/charmhelpers/fetch/__init__.py 2013-11-08 05:49:21 +0000 | |||
784 | @@ -20,6 +20,32 @@ | |||
785 | 20 | PROPOSED_POCKET = """# Proposed | 20 | PROPOSED_POCKET = """# Proposed |
786 | 21 | deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted | 21 | deb http://archive.ubuntu.com/ubuntu {}-proposed main universe multiverse restricted |
787 | 22 | """ | 22 | """ |
788 | 23 | CLOUD_ARCHIVE_POCKETS = { | ||
789 | 24 | # Folsom | ||
790 | 25 | 'folsom': 'precise-updates/folsom', | ||
791 | 26 | 'precise-folsom': 'precise-updates/folsom', | ||
792 | 27 | 'precise-folsom/updates': 'precise-updates/folsom', | ||
793 | 28 | 'precise-updates/folsom': 'precise-updates/folsom', | ||
794 | 29 | 'folsom/proposed': 'precise-proposed/folsom', | ||
795 | 30 | 'precise-folsom/proposed': 'precise-proposed/folsom', | ||
796 | 31 | 'precise-proposed/folsom': 'precise-proposed/folsom', | ||
797 | 32 | # Grizzly | ||
798 | 33 | 'grizzly': 'precise-updates/grizzly', | ||
799 | 34 | 'precise-grizzly': 'precise-updates/grizzly', | ||
800 | 35 | 'precise-grizzly/updates': 'precise-updates/grizzly', | ||
801 | 36 | 'precise-updates/grizzly': 'precise-updates/grizzly', | ||
802 | 37 | 'grizzly/proposed': 'precise-proposed/grizzly', | ||
803 | 38 | 'precise-grizzly/proposed': 'precise-proposed/grizzly', | ||
804 | 39 | 'precise-proposed/grizzly': 'precise-proposed/grizzly', | ||
805 | 40 | # Havana | ||
806 | 41 | 'havana': 'precise-updates/havana', | ||
807 | 42 | 'precise-havana': 'precise-updates/havana', | ||
808 | 43 | 'precise-havana/updates': 'precise-updates/havana', | ||
809 | 44 | 'precise-updates/havana': 'precise-updates/havana', | ||
810 | 45 | 'havana/proposed': 'precise-proposed/havana', | ||
811 | 46 | 'precies-havana/proposed': 'precise-proposed/havana', | ||
812 | 47 | 'precise-proposed/havana': 'precise-proposed/havana', | ||
813 | 48 | } | ||
814 | 23 | 49 | ||
815 | 24 | 50 | ||
816 | 25 | def filter_installed_packages(packages): | 51 | def filter_installed_packages(packages): |
817 | @@ -79,16 +105,35 @@ | |||
818 | 79 | subprocess.call(cmd) | 105 | subprocess.call(cmd) |
819 | 80 | 106 | ||
820 | 81 | 107 | ||
821 | 108 | def apt_hold(packages, fatal=False): | ||
822 | 109 | """Hold one or more packages""" | ||
823 | 110 | cmd = ['apt-mark', 'hold'] | ||
824 | 111 | if isinstance(packages, basestring): | ||
825 | 112 | cmd.append(packages) | ||
826 | 113 | else: | ||
827 | 114 | cmd.extend(packages) | ||
828 | 115 | log("Holding {}".format(packages)) | ||
829 | 116 | if fatal: | ||
830 | 117 | subprocess.check_call(cmd) | ||
831 | 118 | else: | ||
832 | 119 | subprocess.call(cmd) | ||
833 | 120 | |||
834 | 121 | |||
835 | 82 | def add_source(source, key=None): | 122 | def add_source(source, key=None): |
838 | 83 | if ((source.startswith('ppa:') or | 123 | if (source.startswith('ppa:') or |
839 | 84 | source.startswith('http:'))): | 124 | source.startswith('http:') or |
840 | 125 | source.startswith('deb ') or | ||
841 | 126 | source.startswith('cloud-archive:')): | ||
842 | 85 | subprocess.check_call(['add-apt-repository', '--yes', source]) | 127 | subprocess.check_call(['add-apt-repository', '--yes', source]) |
843 | 86 | elif source.startswith('cloud:'): | 128 | elif source.startswith('cloud:'): |
844 | 87 | apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), | 129 | apt_install(filter_installed_packages(['ubuntu-cloud-keyring']), |
845 | 88 | fatal=True) | 130 | fatal=True) |
846 | 89 | pocket = source.split(':')[-1] | 131 | pocket = source.split(':')[-1] |
847 | 132 | if pocket not in CLOUD_ARCHIVE_POCKETS: | ||
848 | 133 | raise SourceConfigError('Unsupported cloud: source option %s' % pocket) | ||
849 | 134 | actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket] | ||
850 | 90 | with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: | 135 | with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: |
852 | 91 | apt.write(CLOUD_ARCHIVE.format(pocket)) | 136 | apt.write(CLOUD_ARCHIVE.format(actual_pocket)) |
853 | 92 | elif source == 'proposed': | 137 | elif source == 'proposed': |
854 | 93 | release = lsb_release()['DISTRIB_CODENAME'] | 138 | release = lsb_release()['DISTRIB_CODENAME'] |
855 | 94 | with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: | 139 | with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt: |
856 | @@ -118,8 +163,11 @@ | |||
857 | 118 | Note that 'null' (a.k.a. None) should not be quoted. | 163 | Note that 'null' (a.k.a. None) should not be quoted. |
858 | 119 | """ | 164 | """ |
859 | 120 | sources = safe_load(config(sources_var)) | 165 | sources = safe_load(config(sources_var)) |
862 | 121 | keys = safe_load(config(keys_var)) | 166 | keys = config(keys_var) |
863 | 122 | if isinstance(sources, basestring) and isinstance(keys, basestring): | 167 | if keys is not None: |
864 | 168 | keys = safe_load(keys) | ||
865 | 169 | if isinstance(sources, basestring) and ( | ||
866 | 170 | keys is None or isinstance(keys, basestring)): | ||
867 | 123 | add_source(sources, keys) | 171 | add_source(sources, keys) |
868 | 124 | else: | 172 | else: |
869 | 125 | if not len(sources) == len(keys): | 173 | if not len(sources) == len(keys): |
870 | 126 | 174 | ||
871 | === modified file 'hooks/charmhelpers/fetch/bzrurl.py' | |||
872 | --- hooks/charmhelpers/fetch/bzrurl.py 2013-09-23 13:23:51 +0000 | |||
873 | +++ hooks/charmhelpers/fetch/bzrurl.py 2013-11-08 05:49:21 +0000 | |||
874 | @@ -12,6 +12,7 @@ | |||
875 | 12 | apt_install("python-bzrlib") | 12 | apt_install("python-bzrlib") |
876 | 13 | from bzrlib.branch import Branch | 13 | from bzrlib.branch import Branch |
877 | 14 | 14 | ||
878 | 15 | |||
879 | 15 | class BzrUrlFetchHandler(BaseFetchHandler): | 16 | class BzrUrlFetchHandler(BaseFetchHandler): |
880 | 16 | """Handler for bazaar branches via generic and lp URLs""" | 17 | """Handler for bazaar branches via generic and lp URLs""" |
881 | 17 | def can_handle(self, source): | 18 | def can_handle(self, source): |
882 | @@ -46,4 +47,3 @@ | |||
883 | 46 | except OSError as e: | 47 | except OSError as e: |
884 | 47 | raise UnhandledSource(e.strerror) | 48 | raise UnhandledSource(e.strerror) |
885 | 48 | return dest_dir | 49 | return dest_dir |
886 | 49 | |||
887 | 50 | 50 | ||
888 | === modified file 'hooks/nova_compute_context.py' | |||
889 | --- hooks/nova_compute_context.py 2013-10-15 12:04:13 +0000 | |||
890 | +++ hooks/nova_compute_context.py 2013-11-08 05:49:21 +0000 | |||
891 | @@ -273,6 +273,14 @@ | |||
892 | 273 | self.network_manager) | 273 | self.network_manager) |
893 | 274 | return ctxt | 274 | return ctxt |
894 | 275 | 275 | ||
895 | 276 | def restart_trigger(self): | ||
896 | 277 | rt = None | ||
897 | 278 | for rid in relation_ids('cloud-compute'): | ||
898 | 279 | for unit in related_units(rid): | ||
899 | 280 | rt = relation_get('restart_trigger', rid=rid, unit=unit) | ||
900 | 281 | if rt: | ||
901 | 282 | return rt | ||
902 | 283 | |||
903 | 276 | def __call__(self): | 284 | def __call__(self): |
904 | 277 | rids = relation_ids('cloud-compute') | 285 | rids = relation_ids('cloud-compute') |
905 | 278 | if not rids: | 286 | if not rids: |
906 | @@ -289,6 +297,9 @@ | |||
907 | 289 | if vol_service: | 297 | if vol_service: |
908 | 290 | ctxt['volume_service'] = vol_service | 298 | ctxt['volume_service'] = vol_service |
909 | 291 | 299 | ||
910 | 300 | if self.restart_trigger(): | ||
911 | 301 | ctxt['restart_trigger'] = self.restart_trigger() | ||
912 | 302 | |||
913 | 292 | return ctxt | 303 | return ctxt |
914 | 293 | 304 | ||
915 | 294 | 305 | ||
916 | 295 | 306 | ||
917 | === modified file 'hooks/nova_compute_hooks.py' | |||
918 | --- hooks/nova_compute_hooks.py 2013-10-17 19:27:44 +0000 | |||
919 | +++ hooks/nova_compute_hooks.py 2013-11-08 05:49:21 +0000 | |||
920 | @@ -48,7 +48,7 @@ | |||
921 | 48 | register_configs, | 48 | register_configs, |
922 | 49 | NOVA_CONF, | 49 | NOVA_CONF, |
923 | 50 | QUANTUM_CONF, NEUTRON_CONF, | 50 | QUANTUM_CONF, NEUTRON_CONF, |
925 | 51 | CEPH_CONF, CEPH_SECRET | 51 | ceph_config_file, CEPH_SECRET |
926 | 52 | ) | 52 | ) |
927 | 53 | 53 | ||
928 | 54 | from nova_compute_context import CEPH_SECRET_UUID | 54 | from nova_compute_context import CEPH_SECRET_UUID |
929 | @@ -94,9 +94,10 @@ | |||
930 | 94 | log('amqp relation incomplete. Peer not ready?') | 94 | log('amqp relation incomplete. Peer not ready?') |
931 | 95 | return | 95 | return |
932 | 96 | CONFIGS.write(NOVA_CONF) | 96 | CONFIGS.write(NOVA_CONF) |
934 | 97 | if network_manager() == 'quantum': | 97 | |
935 | 98 | if network_manager() == 'quantum' and neutron_plugin() == 'ovs': | ||
936 | 98 | CONFIGS.write(QUANTUM_CONF) | 99 | CONFIGS.write(QUANTUM_CONF) |
938 | 99 | if network_manager() == 'neutron': | 100 | if network_manager() == 'neutron' and neutron_plugin() == 'ovs': |
939 | 100 | CONFIGS.write(NEUTRON_CONF) | 101 | CONFIGS.write(NEUTRON_CONF) |
940 | 101 | 102 | ||
941 | 102 | 103 | ||
942 | @@ -106,7 +107,8 @@ | |||
943 | 106 | nova_database=config('database'), | 107 | nova_database=config('database'), |
944 | 107 | nova_username=config('database-user'), | 108 | nova_username=config('database-user'), |
945 | 108 | nova_hostname=unit_get('private-address')) | 109 | nova_hostname=unit_get('private-address')) |
947 | 109 | if network_manager() in ['quantum', 'neutron']: | 110 | if (network_manager() in ['quantum', 'neutron'] |
948 | 111 | and neutron_plugin() == 'ovs'): | ||
949 | 110 | # XXX: Renaming relations from quantum_* to neutron_* here. | 112 | # XXX: Renaming relations from quantum_* to neutron_* here. |
950 | 111 | relation_set(relation_id=rid, | 113 | relation_set(relation_id=rid, |
951 | 112 | neutron_database=config('neutron-database'), | 114 | neutron_database=config('neutron-database'), |
952 | @@ -122,8 +124,8 @@ | |||
953 | 122 | return | 124 | return |
954 | 123 | CONFIGS.write(NOVA_CONF) | 125 | CONFIGS.write(NOVA_CONF) |
955 | 124 | nm = network_manager() | 126 | nm = network_manager() |
958 | 125 | if nm in ['quantum', 'neutron']: | 127 | plugin = neutron_plugin() |
959 | 126 | plugin = neutron_plugin() | 128 | if nm in ['quantum', 'neutron'] and plugin == 'ovs': |
960 | 127 | CONFIGS.write(neutron_plugin_attribute(plugin, 'config', nm)) | 129 | CONFIGS.write(neutron_plugin_attribute(plugin, 'config', nm)) |
961 | 128 | 130 | ||
962 | 129 | 131 | ||
963 | @@ -157,7 +159,8 @@ | |||
964 | 157 | CONFIGS.write_all() | 159 | CONFIGS.write_all() |
965 | 158 | import_authorized_keys() | 160 | import_authorized_keys() |
966 | 159 | import_keystone_ca_cert() | 161 | import_keystone_ca_cert() |
968 | 160 | if network_manager() in ['quantum', 'neutron']: | 162 | if (network_manager() in ['quantum', 'neutron'] |
969 | 163 | and neutron_plugin() == 'ovs'): | ||
970 | 161 | # in case we already have a database relation, need to request | 164 | # in case we already have a database relation, need to request |
971 | 162 | # access to the additional neutron database. | 165 | # access to the additional neutron database. |
972 | 163 | [db_joined(rid) for rid in relation_ids('shared-db')] | 166 | [db_joined(rid) for rid in relation_ids('shared-db')] |
973 | @@ -179,7 +182,7 @@ | |||
974 | 179 | if not ensure_ceph_keyring(service=svc): | 182 | if not ensure_ceph_keyring(service=svc): |
975 | 180 | log('Could not create ceph keyring: peer not ready?') | 183 | log('Could not create ceph keyring: peer not ready?') |
976 | 181 | return | 184 | return |
978 | 182 | CONFIGS.write(CEPH_CONF) | 185 | CONFIGS.write(ceph_config_file()) |
979 | 183 | CONFIGS.write(CEPH_SECRET) | 186 | CONFIGS.write(CEPH_SECRET) |
980 | 184 | CONFIGS.write(NOVA_CONF) | 187 | CONFIGS.write(NOVA_CONF) |
981 | 185 | 188 | ||
982 | 186 | 189 | ||
983 | === modified file 'hooks/nova_compute_utils.py' | |||
984 | --- hooks/nova_compute_utils.py 2013-09-24 17:11:51 +0000 | |||
985 | +++ hooks/nova_compute_utils.py 2013-11-08 05:49:21 +0000 | |||
986 | @@ -6,7 +6,7 @@ | |||
987 | 6 | from subprocess import check_call, check_output | 6 | from subprocess import check_call, check_output |
988 | 7 | 7 | ||
989 | 8 | from charmhelpers.fetch import apt_update, apt_install | 8 | from charmhelpers.fetch import apt_update, apt_install |
991 | 9 | 9 | from charmhelpers.core.host import mkdir | |
992 | 10 | from charmhelpers.core.hookenv import ( | 10 | from charmhelpers.core.hookenv import ( |
993 | 11 | config, | 11 | config, |
994 | 12 | log, | 12 | log, |
995 | @@ -14,10 +14,12 @@ | |||
996 | 14 | relation_ids, | 14 | relation_ids, |
997 | 15 | relation_get, | 15 | relation_get, |
998 | 16 | DEBUG, | 16 | DEBUG, |
999 | 17 | service_name | ||
1000 | 17 | ) | 18 | ) |
1001 | 18 | 19 | ||
1002 | 19 | from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute | 20 | from charmhelpers.contrib.openstack.neutron import neutron_plugin_attribute |
1003 | 20 | from charmhelpers.contrib.openstack import templating, context | 21 | from charmhelpers.contrib.openstack import templating, context |
1004 | 22 | from charmhelpers.contrib.openstack.alternatives import install_alternative | ||
1005 | 21 | 23 | ||
1006 | 22 | from charmhelpers.contrib.openstack.utils import ( | 24 | from charmhelpers.contrib.openstack.utils import ( |
1007 | 23 | configure_installation_source, | 25 | configure_installation_source, |
1008 | @@ -72,13 +74,10 @@ | |||
1009 | 72 | } | 74 | } |
1010 | 73 | 75 | ||
1011 | 74 | CEPH_CONF = '/etc/ceph/ceph.conf' | 76 | CEPH_CONF = '/etc/ceph/ceph.conf' |
1012 | 77 | CHARM_CEPH_CONF = '/var/lib/charm/{}/ceph.conf' | ||
1013 | 75 | CEPH_SECRET = '/etc/ceph/secret.xml' | 78 | CEPH_SECRET = '/etc/ceph/secret.xml' |
1014 | 76 | 79 | ||
1015 | 77 | CEPH_RESOURCES = { | 80 | CEPH_RESOURCES = { |
1016 | 78 | CEPH_CONF: { | ||
1017 | 79 | 'contexts': [NovaComputeCephContext()], | ||
1018 | 80 | 'services': [], | ||
1019 | 81 | }, | ||
1020 | 82 | CEPH_SECRET: { | 81 | CEPH_SECRET: { |
1021 | 83 | 'contexts': [NovaComputeCephContext()], | 82 | 'contexts': [NovaComputeCephContext()], |
1022 | 84 | 'services': [], | 83 | 'services': [], |
1023 | @@ -114,6 +113,10 @@ | |||
1024 | 114 | } | 113 | } |
1025 | 115 | 114 | ||
1026 | 116 | 115 | ||
1027 | 116 | def ceph_config_file(): | ||
1028 | 117 | return CHARM_CEPH_CONF.format(service_name()) | ||
1029 | 118 | |||
1030 | 119 | |||
1031 | 117 | def resource_map(): | 120 | def resource_map(): |
1032 | 118 | ''' | 121 | ''' |
1033 | 119 | Dynamically generate a map of resources that will be managed for a single | 122 | Dynamically generate a map of resources that will be managed for a single |
1034 | @@ -122,6 +125,7 @@ | |||
1035 | 122 | # TODO: Cache this on first call? | 125 | # TODO: Cache this on first call? |
1036 | 123 | resource_map = deepcopy(BASE_RESOURCE_MAP) | 126 | resource_map = deepcopy(BASE_RESOURCE_MAP) |
1037 | 124 | net_manager = network_manager() | 127 | net_manager = network_manager() |
1038 | 128 | plugin = neutron_plugin() | ||
1039 | 125 | 129 | ||
1040 | 126 | # Network manager gets set late by the cloud-compute interface. | 130 | # Network manager gets set late by the cloud-compute interface. |
1041 | 127 | # FlatDHCPManager only requires some extra packages. | 131 | # FlatDHCPManager only requires some extra packages. |
1042 | @@ -133,17 +137,15 @@ | |||
1043 | 133 | 137 | ||
1044 | 134 | # Neutron/quantum requires additional contexts, as well as new resources | 138 | # Neutron/quantum requires additional contexts, as well as new resources |
1045 | 135 | # depending on the plugin used. | 139 | # depending on the plugin used. |
1046 | 140 | # NOTE(james-page): only required for ovs plugin right now | ||
1047 | 136 | if net_manager in ['neutron', 'quantum']: | 141 | if net_manager in ['neutron', 'quantum']: |
1058 | 137 | if net_manager == 'quantum': | 142 | if plugin == 'ovs': |
1059 | 138 | nm_rsc = QUANTUM_RESOURCES | 143 | if net_manager == 'quantum': |
1060 | 139 | if net_manager == 'neutron': | 144 | nm_rsc = QUANTUM_RESOURCES |
1061 | 140 | nm_rsc = NEUTRON_RESOURCES | 145 | if net_manager == 'neutron': |
1062 | 141 | resource_map.update(nm_rsc) | 146 | nm_rsc = NEUTRON_RESOURCES |
1063 | 142 | 147 | resource_map.update(nm_rsc) | |
1064 | 143 | resource_map[NOVA_CONF]['contexts'].append(NeutronComputeContext()) | 148 | |
1055 | 144 | |||
1056 | 145 | plugin = neutron_plugin() | ||
1057 | 146 | if plugin: | ||
1065 | 147 | conf = neutron_plugin_attribute(plugin, 'config', net_manager) | 149 | conf = neutron_plugin_attribute(plugin, 'config', net_manager) |
1066 | 148 | svcs = neutron_plugin_attribute(plugin, 'services', net_manager) | 150 | svcs = neutron_plugin_attribute(plugin, 'services', net_manager) |
1067 | 149 | ctxts = (neutron_plugin_attribute(plugin, 'contexts', net_manager) | 151 | ctxts = (neutron_plugin_attribute(plugin, 'contexts', net_manager) |
1068 | @@ -156,7 +158,25 @@ | |||
1069 | 156 | # associate the plugin agent with main network manager config(s) | 158 | # associate the plugin agent with main network manager config(s) |
1070 | 157 | [resource_map[nmc]['services'].extend(svcs) for nmc in nm_rsc] | 159 | [resource_map[nmc]['services'].extend(svcs) for nmc in nm_rsc] |
1071 | 158 | 160 | ||
1072 | 161 | resource_map[NOVA_CONF]['contexts'].append(NeutronComputeContext()) | ||
1073 | 162 | |||
1074 | 159 | if relation_ids('ceph'): | 163 | if relation_ids('ceph'): |
1075 | 164 | # Add charm ceph configuration to resources and | ||
1076 | 165 | # ensure directory actually exists | ||
1077 | 166 | mkdir(os.path.dirname(ceph_config_file())) | ||
1078 | 167 | mkdir(os.path.dirname(CEPH_CONF)) | ||
1079 | 168 | # Install ceph config as an alternative for co-location with | ||
1080 | 169 | # ceph and ceph-osd charms - nova-compute ceph.conf will be | ||
1081 | 170 | # lower priority that both of these but thats OK | ||
1082 | 171 | if not os.path.exists(ceph_config_file()): | ||
1083 | 172 | # touch file for pre-templated generation | ||
1084 | 173 | open(ceph_config_file(), 'w').close() | ||
1085 | 174 | install_alternative(os.path.basename(CEPH_CONF), | ||
1086 | 175 | CEPH_CONF, ceph_config_file()) | ||
1087 | 176 | CEPH_RESOURCES[ceph_config_file()] = { | ||
1088 | 177 | 'contexts': [NovaComputeCephContext()], | ||
1089 | 178 | 'services': [], | ||
1090 | 179 | } | ||
1091 | 160 | resource_map.update(CEPH_RESOURCES) | 180 | resource_map.update(CEPH_RESOURCES) |
1092 | 161 | 181 | ||
1093 | 162 | return resource_map | 182 | return resource_map |
1094 | 163 | 183 | ||
1095 | === modified file 'templates/essex/nova.conf' | |||
1096 | --- templates/essex/nova.conf 2013-08-01 23:21:58 +0000 | |||
1097 | +++ templates/essex/nova.conf 2013-11-08 05:49:21 +0000 | |||
1098 | @@ -1,6 +1,9 @@ | |||
1099 | 1 | ############################################################################### | 1 | ############################################################################### |
1100 | 2 | # [ WARNING ] | 2 | # [ WARNING ] |
1101 | 3 | # Configuration file maintained by Juju. Local changes may be overwritten. | 3 | # Configuration file maintained by Juju. Local changes may be overwritten. |
1102 | 4 | {% if restart_trigger -%} | ||
1103 | 5 | # restart trigger: {{ restart_trigger }} | ||
1104 | 6 | {% endif -%} | ||
1105 | 4 | ############################################################################### | 7 | ############################################################################### |
1106 | 5 | --dhcpbridge_flagfile=/etc/nova/nova.conf | 8 | --dhcpbridge_flagfile=/etc/nova/nova.conf |
1107 | 6 | --dhcpbridge=/usr/bin/nova-dhcpbridge | 9 | --dhcpbridge=/usr/bin/nova-dhcpbridge |
1108 | 7 | 10 | ||
1109 | === modified file 'templates/folsom/nova.conf' | |||
1110 | --- templates/folsom/nova.conf 2013-10-01 11:16:21 +0000 | |||
1111 | +++ templates/folsom/nova.conf 2013-11-08 05:49:21 +0000 | |||
1112 | @@ -2,6 +2,9 @@ | |||
1113 | 2 | ############################################################################### | 2 | ############################################################################### |
1114 | 3 | # [ WARNING ] | 3 | # [ WARNING ] |
1115 | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. |
1116 | 5 | {% if restart_trigger -%} | ||
1117 | 6 | # restart trigger: {{ restart_trigger }} | ||
1118 | 7 | {% endif -%} | ||
1119 | 5 | ############################################################################### | 8 | ############################################################################### |
1120 | 6 | [DEFAULT] | 9 | [DEFAULT] |
1121 | 7 | dhcpbridge_flagfile=/etc/nova/nova.conf | 10 | dhcpbridge_flagfile=/etc/nova/nova.conf |
1122 | 8 | 11 | ||
1123 | === modified file 'templates/grizzly/nova.conf' | |||
1124 | --- templates/grizzly/nova.conf 2013-10-01 11:16:21 +0000 | |||
1125 | +++ templates/grizzly/nova.conf 2013-11-08 05:49:21 +0000 | |||
1126 | @@ -2,6 +2,9 @@ | |||
1127 | 2 | ############################################################################### | 2 | ############################################################################### |
1128 | 3 | # [ WARNING ] | 3 | # [ WARNING ] |
1129 | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. |
1130 | 5 | {% if restart_trigger -%} | ||
1131 | 6 | # restart trigger: {{ restart_trigger }} | ||
1132 | 7 | {% endif -%} | ||
1133 | 5 | ############################################################################### | 8 | ############################################################################### |
1134 | 6 | [DEFAULT] | 9 | [DEFAULT] |
1135 | 7 | dhcpbridge_flagfile=/etc/nova/nova.conf | 10 | dhcpbridge_flagfile=/etc/nova/nova.conf |
1136 | @@ -52,6 +55,8 @@ | |||
1137 | 52 | 55 | ||
1138 | 53 | {% if neutron_plugin and neutron_plugin == 'nvp' -%} | 56 | {% if neutron_plugin and neutron_plugin == 'nvp' -%} |
1139 | 54 | libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtOpenVswitchVirtualPortDriver | 57 | libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtOpenVswitchVirtualPortDriver |
1140 | 58 | security_group_api = quantum | ||
1141 | 59 | firewall_driver = nova.virt.firewall.NoopFirewallDriver | ||
1142 | 55 | {% endif -%} | 60 | {% endif -%} |
1143 | 56 | 61 | ||
1144 | 57 | {% if network_manager_config -%} | 62 | {% if network_manager_config -%} |
1145 | 58 | 63 | ||
1146 | === modified file 'templates/havana/nova.conf' | |||
1147 | --- templates/havana/nova.conf 2013-10-01 11:16:21 +0000 | |||
1148 | +++ templates/havana/nova.conf 2013-11-08 05:49:21 +0000 | |||
1149 | @@ -2,6 +2,9 @@ | |||
1150 | 2 | ############################################################################### | 2 | ############################################################################### |
1151 | 3 | # [ WARNING ] | 3 | # [ WARNING ] |
1152 | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. |
1153 | 5 | {% if restart_trigger -%} | ||
1154 | 6 | # restart trigger: {{ restart_trigger }} | ||
1155 | 7 | {% endif -%} | ||
1156 | 5 | ############################################################################### | 8 | ############################################################################### |
1157 | 6 | [DEFAULT] | 9 | [DEFAULT] |
1158 | 7 | dhcpbridge_flagfile=/etc/nova/nova.conf | 10 | dhcpbridge_flagfile=/etc/nova/nova.conf |
1159 | @@ -52,6 +55,8 @@ | |||
1160 | 52 | 55 | ||
1161 | 53 | {% if neutron_plugin and neutron_plugin == 'nvp' -%} | 56 | {% if neutron_plugin and neutron_plugin == 'nvp' -%} |
1162 | 54 | libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtOpenVswitchVirtualPortDriver | 57 | libvirt_vif_driver = nova.virt.libvirt.vif.LibvirtOpenVswitchVirtualPortDriver |
1163 | 58 | security_group_api = neutron | ||
1164 | 59 | firewall_driver = nova.virt.firewall.NoopFirewallDriver | ||
1165 | 55 | {% endif -%} | 60 | {% endif -%} |
1166 | 56 | 61 | ||
1167 | 57 | {% if network_manager_config -%} | 62 | {% if network_manager_config -%} |
1168 | 58 | 63 | ||
1169 | === modified file 'unit_tests/test_nova_compute_contexts.py' | |||
1170 | --- unit_tests/test_nova_compute_contexts.py 2013-09-25 09:01:42 +0000 | |||
1171 | +++ unit_tests/test_nova_compute_contexts.py 2013-11-08 05:49:21 +0000 | |||
1172 | @@ -67,6 +67,20 @@ | |||
1173 | 67 | self.assertEquals({}, cloud_compute()) | 67 | self.assertEquals({}, cloud_compute()) |
1174 | 68 | 68 | ||
1175 | 69 | @patch.object(context, '_network_manager') | 69 | @patch.object(context, '_network_manager') |
1176 | 70 | def test_cloud_compute_context_restart_trigger(self, nm): | ||
1177 | 71 | nm.return_value = None | ||
1178 | 72 | cloud_compute = context.CloudComputeContext() | ||
1179 | 73 | with patch.object(cloud_compute, 'restart_trigger') as rt: | ||
1180 | 74 | rt.return_value = 'footrigger' | ||
1181 | 75 | ctxt = cloud_compute() | ||
1182 | 76 | self.assertEquals(ctxt.get('restart_trigger'), 'footrigger') | ||
1183 | 77 | |||
1184 | 78 | with patch.object(cloud_compute, 'restart_trigger') as rt: | ||
1185 | 79 | rt.return_value = None | ||
1186 | 80 | ctxt = cloud_compute() | ||
1187 | 81 | self.assertEquals(ctxt.get('restart_trigger'), None) | ||
1188 | 82 | |||
1189 | 83 | @patch.object(context, '_network_manager') | ||
1190 | 70 | def test_cloud_compute_volume_context_cinder(self, netman): | 84 | def test_cloud_compute_volume_context_cinder(self, netman): |
1191 | 71 | netman.return_value = None | 85 | netman.return_value = None |
1192 | 72 | self.relation_ids.return_value = 'cloud-compute:0' | 86 | self.relation_ids.return_value = 'cloud-compute:0' |
1193 | 73 | 87 | ||
1194 | === modified file 'unit_tests/test_nova_compute_hooks.py' | |||
1195 | --- unit_tests/test_nova_compute_hooks.py 2013-10-17 19:25:15 +0000 | |||
1196 | +++ unit_tests/test_nova_compute_hooks.py 2013-11-08 05:49:21 +0000 | |||
1197 | @@ -123,6 +123,7 @@ | |||
1198 | 123 | configs.write = MagicMock() | 123 | configs.write = MagicMock() |
1199 | 124 | if quantum: | 124 | if quantum: |
1200 | 125 | self.network_manager.return_value = 'quantum' | 125 | self.network_manager.return_value = 'quantum' |
1201 | 126 | self.neutron_plugin.return_value = 'ovs' | ||
1202 | 126 | hooks.amqp_changed() | 127 | hooks.amqp_changed() |
1203 | 127 | 128 | ||
1204 | 128 | @patch.object(hooks, 'CONFIGS') | 129 | @patch.object(hooks, 'CONFIGS') |
1205 | @@ -147,9 +148,10 @@ | |||
1206 | 147 | nova_hostname='nova.foohost.com') | 148 | nova_hostname='nova.foohost.com') |
1207 | 148 | self.unit_get.assert_called_with('private-address') | 149 | self.unit_get.assert_called_with('private-address') |
1208 | 149 | 150 | ||
1210 | 150 | def test_db_joined_quantum(self): | 151 | def test_db_joined_quantum_ovs(self): |
1211 | 151 | self.unit_get.return_value = 'nova.foohost.com' | 152 | self.unit_get.return_value = 'nova.foohost.com' |
1212 | 152 | self.network_manager.return_value = 'quantum' | 153 | self.network_manager.return_value = 'quantum' |
1213 | 154 | self.neutron_plugin.return_value = 'ovs' | ||
1214 | 153 | hooks.db_joined(rid='shared-db:0') | 155 | hooks.db_joined(rid='shared-db:0') |
1215 | 154 | calls = [call(nova_database='nova', | 156 | calls = [call(nova_database='nova', |
1216 | 155 | nova_username='nova', | 157 | nova_username='nova', |
1217 | @@ -163,6 +165,21 @@ | |||
1218 | 163 | for c in calls] | 165 | for c in calls] |
1219 | 164 | self.unit_get.assert_called_with('private-address') | 166 | self.unit_get.assert_called_with('private-address') |
1220 | 165 | 167 | ||
1221 | 168 | def test_db_joined_quantum_nvp(self): | ||
1222 | 169 | self.unit_get.return_value = 'nova.foohost.com' | ||
1223 | 170 | self.network_manager.return_value = 'quantum' | ||
1224 | 171 | self.neutron_plugin.return_value = 'nvp' | ||
1225 | 172 | hooks.db_joined(rid='shared-db:0') | ||
1226 | 173 | calls = [call(nova_database='nova', | ||
1227 | 174 | nova_username='nova', | ||
1228 | 175 | nova_hostname='nova.foohost.com', | ||
1229 | 176 | relation_id='shared-db:0')] | ||
1230 | 177 | # NVP plugin requires no DB access - check it was not | ||
1231 | 178 | # requested | ||
1232 | 179 | [self.assertIn(c, self.relation_set.call_args_list) | ||
1233 | 180 | for c in calls] | ||
1234 | 181 | self.unit_get.assert_called_with('private-address') | ||
1235 | 182 | |||
1236 | 166 | @patch.object(hooks, 'CONFIGS') | 183 | @patch.object(hooks, 'CONFIGS') |
1237 | 167 | def test_db_changed_missing_relation_data(self, configs): | 184 | def test_db_changed_missing_relation_data(self, configs): |
1238 | 168 | configs.complete_contexts = MagicMock() | 185 | configs.complete_contexts = MagicMock() |
1239 | @@ -187,13 +204,23 @@ | |||
1240 | 187 | configs.write.call_args_list) | 204 | configs.write.call_args_list) |
1241 | 188 | 205 | ||
1242 | 189 | @patch.object(hooks, 'CONFIGS') | 206 | @patch.object(hooks, 'CONFIGS') |
1244 | 190 | def test_db_changed_with_data_and_quantum(self, configs): | 207 | def test_db_changed_with_data_and_quantum_ovs(self, configs): |
1245 | 191 | self.neutron_plugin_attribute.return_value = '/etc/quantum/plugin.conf' | 208 | self.neutron_plugin_attribute.return_value = '/etc/quantum/plugin.conf' |
1246 | 209 | self.neutron_plugin.return_value = 'ovs' | ||
1247 | 192 | self._shared_db_test(configs, quantum=True) | 210 | self._shared_db_test(configs, quantum=True) |
1248 | 193 | ex = [call('/etc/nova/nova.conf'), call('/etc/quantum/plugin.conf')] | 211 | ex = [call('/etc/nova/nova.conf'), call('/etc/quantum/plugin.conf')] |
1249 | 194 | self.assertEquals(ex, configs.write.call_args_list) | 212 | self.assertEquals(ex, configs.write.call_args_list) |
1250 | 195 | 213 | ||
1251 | 196 | @patch.object(hooks, 'CONFIGS') | 214 | @patch.object(hooks, 'CONFIGS') |
1252 | 215 | def test_db_changed_with_data_and_quantum_nvp(self, configs): | ||
1253 | 216 | self.neutron_plugin_attribute.return_value = '/etc/quantum/plugin.conf' | ||
1254 | 217 | self.neutron_plugin.return_value = 'nvp' | ||
1255 | 218 | self._shared_db_test(configs, quantum=True) | ||
1256 | 219 | ex = [call('/etc/nova/nova.conf')] | ||
1257 | 220 | # NVP has no compute agent for neutron; check no config files generated | ||
1258 | 221 | self.assertEquals(ex, configs.write.call_args_list) | ||
1259 | 222 | |||
1260 | 223 | @patch.object(hooks, 'CONFIGS') | ||
1261 | 197 | def test_image_service_missing_relation_data(self, configs): | 224 | def test_image_service_missing_relation_data(self, configs): |
1262 | 198 | configs.complete_contexts = MagicMock() | 225 | configs.complete_contexts = MagicMock() |
1263 | 199 | configs.complete_contexts.return_value = [] | 226 | configs.complete_contexts.return_value = [] |
1264 | @@ -264,15 +291,18 @@ | |||
1265 | 264 | 'Could not create ceph keyring: peer not ready?' | 291 | 'Could not create ceph keyring: peer not ready?' |
1266 | 265 | ) | 292 | ) |
1267 | 266 | 293 | ||
1268 | 294 | @patch.object(utils, 'service_name') | ||
1269 | 267 | @patch.object(hooks, 'CONFIGS') | 295 | @patch.object(hooks, 'CONFIGS') |
1271 | 268 | def test_ceph_changed_with_key_and_relation_data(self, configs): | 296 | def test_ceph_changed_with_key_and_relation_data(self, configs, |
1272 | 297 | service_name): | ||
1273 | 269 | configs.complete_contexts = MagicMock() | 298 | configs.complete_contexts = MagicMock() |
1274 | 270 | configs.complete_contexts.return_value = ['ceph'] | 299 | configs.complete_contexts.return_value = ['ceph'] |
1275 | 271 | configs.write = MagicMock() | 300 | configs.write = MagicMock() |
1276 | 301 | service_name.return_value = 'nova-compute' | ||
1277 | 272 | self.ensure_ceph_keyring.return_value = True | 302 | self.ensure_ceph_keyring.return_value = True |
1278 | 273 | hooks.ceph_changed() | 303 | hooks.ceph_changed() |
1279 | 274 | ex = [ | 304 | ex = [ |
1281 | 275 | call('/etc/ceph/ceph.conf'), | 305 | call('/var/lib/charm/nova-compute/ceph.conf'), |
1282 | 276 | call('/etc/ceph/secret.xml'), | 306 | call('/etc/ceph/secret.xml'), |
1283 | 277 | call('/etc/nova/nova.conf'), | 307 | call('/etc/nova/nova.conf'), |
1284 | 278 | ] | 308 | ] |
1285 | 279 | 309 | ||
1286 | === modified file 'unit_tests/test_nova_compute_utils.py' | |||
1287 | --- unit_tests/test_nova_compute_utils.py 2013-09-25 09:01:42 +0000 | |||
1288 | +++ unit_tests/test_nova_compute_utils.py 2013-11-08 05:49:21 +0000 | |||
1289 | @@ -13,6 +13,9 @@ | |||
1290 | 13 | 'related_units', | 13 | 'related_units', |
1291 | 14 | 'relation_ids', | 14 | 'relation_ids', |
1292 | 15 | 'relation_get', | 15 | 'relation_get', |
1293 | 16 | 'service_name', | ||
1294 | 17 | 'mkdir', | ||
1295 | 18 | 'install_alternative' | ||
1296 | 16 | ] | 19 | ] |
1297 | 17 | 20 | ||
1298 | 18 | OVS_PKGS = [ | 21 | OVS_PKGS = [ |
1299 | @@ -25,6 +28,7 @@ | |||
1300 | 25 | def setUp(self): | 28 | def setUp(self): |
1301 | 26 | super(NovaComputeUtilsTests, self).setUp(utils, TO_PATCH) | 29 | super(NovaComputeUtilsTests, self).setUp(utils, TO_PATCH) |
1302 | 27 | self.config.side_effect = self.test_config.get | 30 | self.config.side_effect = self.test_config.get |
1303 | 31 | self.service_name.return_value = 'nova-compute' | ||
1304 | 28 | 32 | ||
1305 | 29 | @patch.object(utils, 'network_manager') | 33 | @patch.object(utils, 'network_manager') |
1306 | 30 | def test_determine_packages_nova_network(self, net_man): | 34 | def test_determine_packages_nova_network(self, net_man): |