Merge lp:~openstack-charmers/charms/precise/quantum-gateway/ha-support into lp:~charmers/charms/precise/quantum-gateway/trunk
- Precise Pangolin (12.04)
- ha-support
- Merge into trunk
Proposed by
James Page
Status: | Merged |
---|---|
Merged at revision: | 33 |
Proposed branch: | lp:~openstack-charmers/charms/precise/quantum-gateway/ha-support |
Merge into: | lp:~charmers/charms/precise/quantum-gateway/trunk |
Diff against target: |
1722 lines (+1181/-278) 16 files modified
.pydevproject (+2/-3) hooks/hooks.py (+155/-19) hooks/lib/cluster_utils.py (+130/-0) hooks/lib/openstack_common.py (+230/-0) hooks/lib/utils.py (+359/-0) hooks/quantum_utils.py (+171/-14) hooks/utils.py (+0/-237) metadata.yaml (+4/-1) revision (+1/-1) templates/evacuate_unit.py (+70/-0) templates/ext-port.conf (+9/-0) templates/l3_agent.ini (+1/-1) templates/metadata_agent.ini (+17/-0) templates/nova.conf (+25/-0) templates/ovs_quantum_plugin.ini (+1/-1) templates/quantum.conf (+6/-1) |
To merge this branch: | bzr merge lp:~openstack-charmers/charms/precise/quantum-gateway/ha-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
charmers | Pending | ||
Review via email: mp+166333@code.launchpad.net |
Commit message
Description of the change
Support for Grizzly
Support for High Avaliability
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.pydevproject' |
2 | --- .pydevproject 2012-12-06 10:22:24 +0000 |
3 | +++ .pydevproject 2013-05-29 17:55:39 +0000 |
4 | @@ -1,10 +1,9 @@ |
5 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
6 | -<?eclipse-pydev version="1.0"?> |
7 | - |
8 | -<pydev_project> |
9 | +<?eclipse-pydev version="1.0"?><pydev_project> |
10 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property> |
11 | <pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property> |
12 | <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH"> |
13 | <path>/quantum-gateway/hooks</path> |
14 | +<path>/quantum-gateway/templates</path> |
15 | </pydev_pathproperty> |
16 | </pydev_project> |
17 | |
18 | === added symlink 'hooks/cluster-relation-departed' |
19 | === target is u'hooks.py' |
20 | === added symlink 'hooks/ha-relation-joined' |
21 | === target is u'hooks.py' |
22 | === modified file 'hooks/hooks.py' |
23 | --- hooks/hooks.py 2012-12-03 15:16:55 +0000 |
24 | +++ hooks/hooks.py 2013-05-29 17:55:39 +0000 |
25 | @@ -1,6 +1,8 @@ |
26 | #!/usr/bin/python |
27 | |
28 | -import utils |
29 | +import lib.utils as utils |
30 | +import lib.cluster_utils as cluster |
31 | +import lib.openstack_common as openstack |
32 | import sys |
33 | import quantum_utils as qutils |
34 | import os |
35 | @@ -21,18 +23,31 @@ |
36 | sys.exit(1) |
37 | |
38 | |
39 | +@utils.inteli_restart(qutils.RESTART_MAP) |
40 | def config_changed(): |
41 | + src = utils.config_get('openstack-origin') |
42 | + available = openstack.get_os_codename_install_source(src) |
43 | + installed = openstack.get_os_codename_package('quantum-common') |
44 | + if (available and |
45 | + openstack.get_os_version_codename(available) > \ |
46 | + openstack.get_os_version_codename(installed)): |
47 | + qutils.do_openstack_upgrade() |
48 | + |
49 | if PLUGIN in qutils.GATEWAY_PKGS.keys(): |
50 | render_quantum_conf() |
51 | + render_dhcp_agent_conf() |
52 | + render_l3_agent_conf() |
53 | + render_metadata_agent_conf() |
54 | + render_metadata_api_conf() |
55 | render_plugin_conf() |
56 | - render_l3_agent_conf() |
57 | + render_ext_port_upstart() |
58 | + render_evacuate_unit() |
59 | if PLUGIN == qutils.OVS: |
60 | qutils.add_bridge(qutils.INT_BRIDGE) |
61 | qutils.add_bridge(qutils.EXT_BRIDGE) |
62 | ext_port = utils.config_get('ext-port') |
63 | if ext_port: |
64 | qutils.add_bridge_port(qutils.EXT_BRIDGE, ext_port) |
65 | - utils.restart(*qutils.GATEWAY_AGENTS[PLUGIN]) |
66 | else: |
67 | utils.juju_log('ERROR', |
68 | 'Please provide a valid plugin config') |
69 | @@ -44,6 +59,19 @@ |
70 | config_changed() |
71 | |
72 | |
73 | +def render_ext_port_upstart(): |
74 | + if utils.config_get('ext-port'): |
75 | + with open(qutils.EXT_PORT_CONF, "w") as conf: |
76 | + conf.write(utils.render_template( |
77 | + os.path.basename(qutils.EXT_PORT_CONF), |
78 | + {"ext_port": utils.config_get('ext-port')} |
79 | + ) |
80 | + ) |
81 | + else: |
82 | + if os.path.exists(qutils.EXT_PORT_CONF): |
83 | + os.remove(qutils.EXT_PORT_CONF) |
84 | + |
85 | + |
86 | def render_l3_agent_conf(): |
87 | context = get_keystone_conf() |
88 | if (context and |
89 | @@ -56,6 +84,30 @@ |
90 | ) |
91 | |
92 | |
93 | +def render_dhcp_agent_conf(): |
94 | + if (os.path.exists(qutils.DHCP_AGENT_CONF)): |
95 | + with open(qutils.DHCP_AGENT_CONF, "w") as conf: |
96 | + conf.write(utils.render_template( |
97 | + os.path.basename(qutils.DHCP_AGENT_CONF), |
98 | + {} |
99 | + ) |
100 | + ) |
101 | + |
102 | + |
103 | +def render_metadata_agent_conf(): |
104 | + context = get_keystone_conf() |
105 | + if (context and |
106 | + os.path.exists(qutils.METADATA_AGENT_CONF)): |
107 | + context['local_ip'] = utils.get_host_ip() |
108 | + context['shared_secret'] = qutils.get_shared_secret() |
109 | + with open(qutils.METADATA_AGENT_CONF, "w") as conf: |
110 | + conf.write(utils.render_template( |
111 | + os.path.basename(qutils.METADATA_AGENT_CONF), |
112 | + context |
113 | + ) |
114 | + ) |
115 | + |
116 | + |
117 | def render_quantum_conf(): |
118 | context = get_rabbit_conf() |
119 | if (context and |
120 | @@ -71,7 +123,7 @@ |
121 | |
122 | |
123 | def render_plugin_conf(): |
124 | - context = get_db_conf() |
125 | + context = get_quantum_db_conf() |
126 | if (context and |
127 | os.path.exists(qutils.PLUGIN_CONF[PLUGIN])): |
128 | context['local_ip'] = utils.get_host_ip() |
129 | @@ -84,6 +136,31 @@ |
130 | ) |
131 | |
132 | |
133 | +def render_metadata_api_conf(): |
134 | + context = get_nova_db_conf() |
135 | + r_context = get_rabbit_conf() |
136 | + q_context = get_keystone_conf() |
137 | + if (context and r_context and q_context and |
138 | + os.path.exists(qutils.NOVA_CONF)): |
139 | + context.update(r_context) |
140 | + context.update(q_context) |
141 | + context['shared_secret'] = qutils.get_shared_secret() |
142 | + with open(qutils.NOVA_CONF, "w") as conf: |
143 | + conf.write(utils.render_template( |
144 | + os.path.basename(qutils.NOVA_CONF), |
145 | + context |
146 | + ) |
147 | + ) |
148 | + |
149 | + |
150 | +def render_evacuate_unit(): |
151 | + context = get_keystone_conf() |
152 | + if context: |
153 | + with open('/usr/local/bin/quantum-evacuate-unit', "w") as conf: |
154 | + conf.write(utils.render_template('evacuate_unit.py', context)) |
155 | + os.chmod('/usr/local/bin/quantum-evacuate-unit', 0700) |
156 | + |
157 | + |
158 | def get_keystone_conf(): |
159 | for relid in utils.relation_ids('quantum-network-service'): |
160 | for unit in utils.relation_list(relid): |
161 | @@ -98,7 +175,15 @@ |
162 | "service_password": utils.relation_get('service_password', |
163 | unit, relid), |
164 | "service_tenant": utils.relation_get('service_tenant', |
165 | - unit, relid) |
166 | + unit, relid), |
167 | + "quantum_host": utils.relation_get('quantum_host', |
168 | + unit, relid), |
169 | + "quantum_port": utils.relation_get('quantum_port', |
170 | + unit, relid), |
171 | + "quantum_url": utils.relation_get('quantum_url', |
172 | + unit, relid), |
173 | + "region": utils.relation_get('region', |
174 | + unit, relid) |
175 | } |
176 | if None not in conf.itervalues(): |
177 | return conf |
178 | @@ -106,24 +191,28 @@ |
179 | |
180 | |
181 | def db_joined(): |
182 | - utils.relation_set(username=qutils.DB_USER, |
183 | - database=qutils.QUANTUM_DB, |
184 | - hostname=utils.unit_get('private-address')) |
185 | - |
186 | - |
187 | + utils.relation_set(quantum_username=qutils.DB_USER, |
188 | + quantum_database=qutils.QUANTUM_DB, |
189 | + quantum_hostname=utils.unit_get('private-address'), |
190 | + nova_username=qutils.NOVA_DB_USER, |
191 | + nova_database=qutils.NOVA_DB, |
192 | + nova_hostname=utils.unit_get('private-address')) |
193 | + |
194 | + |
195 | +@utils.inteli_restart(qutils.RESTART_MAP) |
196 | def db_changed(): |
197 | render_plugin_conf() |
198 | - utils.restart(*qutils.GATEWAY_AGENTS[PLUGIN]) |
199 | - |
200 | - |
201 | -def get_db_conf(): |
202 | + render_metadata_api_conf() |
203 | + |
204 | + |
205 | +def get_quantum_db_conf(): |
206 | for relid in utils.relation_ids('shared-db'): |
207 | for unit in utils.relation_list(relid): |
208 | conf = { |
209 | - "host": utils.relation_get('private-address', |
210 | + "host": utils.relation_get('db_host', |
211 | unit, relid), |
212 | "user": qutils.DB_USER, |
213 | - "password": utils.relation_get('password', |
214 | + "password": utils.relation_get('quantum_password', |
215 | unit, relid), |
216 | "db": qutils.QUANTUM_DB |
217 | } |
218 | @@ -132,14 +221,32 @@ |
219 | return None |
220 | |
221 | |
222 | +def get_nova_db_conf(): |
223 | + for relid in utils.relation_ids('shared-db'): |
224 | + for unit in utils.relation_list(relid): |
225 | + conf = { |
226 | + "host": utils.relation_get('db_host', |
227 | + unit, relid), |
228 | + "user": qutils.NOVA_DB_USER, |
229 | + "password": utils.relation_get('nova_password', |
230 | + unit, relid), |
231 | + "db": qutils.NOVA_DB |
232 | + } |
233 | + if None not in conf.itervalues(): |
234 | + return conf |
235 | + return None |
236 | + |
237 | + |
238 | def amqp_joined(): |
239 | utils.relation_set(username=qutils.RABBIT_USER, |
240 | vhost=qutils.RABBIT_VHOST) |
241 | |
242 | |
243 | +@utils.inteli_restart(qutils.RESTART_MAP) |
244 | def amqp_changed(): |
245 | + render_dhcp_agent_conf() |
246 | render_quantum_conf() |
247 | - utils.restart(*qutils.GATEWAY_AGENTS[PLUGIN]) |
248 | + render_metadata_api_conf() |
249 | |
250 | |
251 | def get_rabbit_conf(): |
252 | @@ -153,15 +260,43 @@ |
253 | "rabbit_password": utils.relation_get('password', |
254 | unit, relid) |
255 | } |
256 | + clustered = utils.relation_get('clustered', unit, relid) |
257 | + if clustered: |
258 | + conf['rabbit_host'] = utils.relation_get('vip', unit, relid) |
259 | if None not in conf.itervalues(): |
260 | return conf |
261 | return None |
262 | |
263 | |
264 | +@utils.inteli_restart(qutils.RESTART_MAP) |
265 | def nm_changed(): |
266 | + render_dhcp_agent_conf() |
267 | render_l3_agent_conf() |
268 | - utils.restart(*qutils.GATEWAY_AGENTS[PLUGIN]) |
269 | - |
270 | + render_metadata_agent_conf() |
271 | + render_metadata_api_conf() |
272 | + render_evacuate_unit() |
273 | + store_ca_cert() |
274 | + |
275 | + |
276 | +def store_ca_cert(): |
277 | + ca_cert = get_ca_cert() |
278 | + if ca_cert: |
279 | + qutils.install_ca(ca_cert) |
280 | + |
281 | + |
282 | +def get_ca_cert(): |
283 | + for relid in utils.relation_ids('quantum-network-service'): |
284 | + for unit in utils.relation_list(relid): |
285 | + ca_cert = utils.relation_get('ca_cert', unit, relid) |
286 | + if ca_cert: |
287 | + return ca_cert |
288 | + return None |
289 | + |
290 | + |
291 | +def cluster_departed(): |
292 | + conf = get_keystone_conf() |
293 | + if conf and cluster.eligible_leader(None): |
294 | + qutils.reassign_agent_resources(conf) |
295 | |
296 | utils.do_hooks({ |
297 | "install": install, |
298 | @@ -172,6 +307,7 @@ |
299 | "amqp-relation-joined": amqp_joined, |
300 | "amqp-relation-changed": amqp_changed, |
301 | "quantum-network-service-relation-changed": nm_changed, |
302 | + "cluster-relation-departed": cluster_departed |
303 | }) |
304 | |
305 | sys.exit(0) |
306 | |
307 | === added directory 'hooks/lib' |
308 | === added file 'hooks/lib/__init__.py' |
309 | === added file 'hooks/lib/cluster_utils.py' |
310 | --- hooks/lib/cluster_utils.py 1970-01-01 00:00:00 +0000 |
311 | +++ hooks/lib/cluster_utils.py 2013-05-29 17:55:39 +0000 |
312 | @@ -0,0 +1,130 @@ |
313 | +# |
314 | +# Copyright 2012 Canonical Ltd. |
315 | +# |
316 | +# This file is sourced from lp:openstack-charm-helpers |
317 | +# |
318 | +# Authors: |
319 | +# James Page <james.page@ubuntu.com> |
320 | +# Adam Gandelman <adamg@ubuntu.com> |
321 | +# |
322 | + |
323 | +from lib.utils import ( |
324 | + juju_log, |
325 | + relation_ids, |
326 | + relation_list, |
327 | + relation_get, |
328 | + get_unit_hostname, |
329 | + config_get |
330 | + ) |
331 | +import subprocess |
332 | +import os |
333 | + |
334 | + |
335 | +def is_clustered(): |
336 | + for r_id in (relation_ids('ha') or []): |
337 | + for unit in (relation_list(r_id) or []): |
338 | + clustered = relation_get('clustered', |
339 | + rid=r_id, |
340 | + unit=unit) |
341 | + if clustered: |
342 | + return True |
343 | + return False |
344 | + |
345 | + |
346 | +def is_leader(resource): |
347 | + cmd = [ |
348 | + "crm", "resource", |
349 | + "show", resource |
350 | + ] |
351 | + try: |
352 | + status = subprocess.check_output(cmd) |
353 | + except subprocess.CalledProcessError: |
354 | + return False |
355 | + else: |
356 | + if get_unit_hostname() in status: |
357 | + return True |
358 | + else: |
359 | + return False |
360 | + |
361 | + |
362 | +def peer_units(): |
363 | + peers = [] |
364 | + for r_id in (relation_ids('cluster') or []): |
365 | + for unit in (relation_list(r_id) or []): |
366 | + peers.append(unit) |
367 | + return peers |
368 | + |
369 | + |
370 | +def oldest_peer(peers): |
371 | + local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1]) |
372 | + for peer in peers: |
373 | + remote_unit_no = int(peer.split('/')[1]) |
374 | + if remote_unit_no < local_unit_no: |
375 | + return False |
376 | + return True |
377 | + |
378 | + |
379 | +def eligible_leader(resource): |
380 | + if is_clustered(): |
381 | + if not is_leader(resource): |
382 | + juju_log('INFO', 'Deferring action to CRM leader.') |
383 | + return False |
384 | + else: |
385 | + peers = peer_units() |
386 | + if peers and not oldest_peer(peers): |
387 | + juju_log('INFO', 'Deferring action to oldest service unit.') |
388 | + return False |
389 | + return True |
390 | + |
391 | + |
392 | +def https(): |
393 | + ''' |
394 | + Determines whether enough data has been provided in configuration |
395 | + or relation data to configure HTTPS |
396 | + . |
397 | + returns: boolean |
398 | + ''' |
399 | + if config_get('use-https') == "yes": |
400 | + return True |
401 | + if config_get('ssl_cert') and config_get('ssl_key'): |
402 | + return True |
403 | + for r_id in relation_ids('identity-service'): |
404 | + for unit in relation_list(r_id): |
405 | + if (relation_get('https_keystone', rid=r_id, unit=unit) and |
406 | + relation_get('ssl_cert', rid=r_id, unit=unit) and |
407 | + relation_get('ssl_key', rid=r_id, unit=unit) and |
408 | + relation_get('ca_cert', rid=r_id, unit=unit)): |
409 | + return True |
410 | + return False |
411 | + |
412 | + |
413 | +def determine_api_port(public_port): |
414 | + ''' |
415 | + Determine correct API server listening port based on |
416 | + existence of HTTPS reverse proxy and/or haproxy. |
417 | + |
418 | + public_port: int: standard public port for given service |
419 | + |
420 | + returns: int: the correct listening port for the API service |
421 | + ''' |
422 | + i = 0 |
423 | + if len(peer_units()) > 0 or is_clustered(): |
424 | + i += 1 |
425 | + if https(): |
426 | + i += 1 |
427 | + return public_port - (i * 10) |
428 | + |
429 | + |
430 | +def determine_haproxy_port(public_port): |
431 | + ''' |
432 | + Description: Determine correct proxy listening port based on public IP + |
433 | + existence of HTTPS reverse proxy. |
434 | + |
435 | + public_port: int: standard public port for given service |
436 | + |
437 | + returns: int: the correct listening port for the HAProxy service |
438 | + ''' |
439 | + i = 0 |
440 | + if https(): |
441 | + i += 1 |
442 | + return public_port - (i * 10) |
443 | |
444 | === added file 'hooks/lib/openstack_common.py' |
445 | --- hooks/lib/openstack_common.py 1970-01-01 00:00:00 +0000 |
446 | +++ hooks/lib/openstack_common.py 2013-05-29 17:55:39 +0000 |
447 | @@ -0,0 +1,230 @@ |
448 | +#!/usr/bin/python |
449 | + |
450 | +# Common python helper functions used for OpenStack charms. |
451 | + |
452 | +import apt_pkg as apt |
453 | +import subprocess |
454 | +import os |
455 | + |
456 | +CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" |
457 | +CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' |
458 | + |
459 | +ubuntu_openstack_release = { |
460 | + 'oneiric': 'diablo', |
461 | + 'precise': 'essex', |
462 | + 'quantal': 'folsom', |
463 | + 'raring': 'grizzly', |
464 | +} |
465 | + |
466 | + |
467 | +openstack_codenames = { |
468 | + '2011.2': 'diablo', |
469 | + '2012.1': 'essex', |
470 | + '2012.2': 'folsom', |
471 | + '2013.1': 'grizzly', |
472 | + '2013.2': 'havana', |
473 | +} |
474 | + |
475 | +# The ugly duckling |
476 | +swift_codenames = { |
477 | + '1.4.3': 'diablo', |
478 | + '1.4.8': 'essex', |
479 | + '1.7.4': 'folsom', |
480 | + '1.7.6': 'grizzly', |
481 | + '1.7.7': 'grizzly', |
482 | + '1.8.0': 'grizzly', |
483 | +} |
484 | + |
485 | + |
486 | +def juju_log(msg): |
487 | + subprocess.check_call(['juju-log', msg]) |
488 | + |
489 | + |
490 | +def error_out(msg): |
491 | + juju_log("FATAL ERROR: %s" % msg) |
492 | + exit(1) |
493 | + |
494 | + |
495 | +def lsb_release(): |
496 | + '''Return /etc/lsb-release in a dict''' |
497 | + lsb = open('/etc/lsb-release', 'r') |
498 | + d = {} |
499 | + for l in lsb: |
500 | + k, v = l.split('=') |
501 | + d[k.strip()] = v.strip() |
502 | + return d |
503 | + |
504 | + |
505 | +def get_os_codename_install_source(src): |
506 | + '''Derive OpenStack release codename from a given installation source.''' |
507 | + ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
508 | + |
509 | + rel = '' |
510 | + if src == 'distro': |
511 | + try: |
512 | + rel = ubuntu_openstack_release[ubuntu_rel] |
513 | + except KeyError: |
514 | + e = 'Code not derive openstack release for '\ |
515 | + 'this Ubuntu release: %s' % rel |
516 | + error_out(e) |
517 | + return rel |
518 | + |
519 | + if src.startswith('cloud:'): |
520 | + ca_rel = src.split(':')[1] |
521 | + ca_rel = ca_rel.split('%s-' % ubuntu_rel)[1].split('/')[0] |
522 | + return ca_rel |
523 | + |
524 | + # Best guess match based on deb string provided |
525 | + if src.startswith('deb') or src.startswith('ppa'): |
526 | + for k, v in openstack_codenames.iteritems(): |
527 | + if v in src: |
528 | + return v |
529 | + |
530 | + |
531 | +def get_os_codename_version(vers): |
532 | + '''Determine OpenStack codename from version number.''' |
533 | + try: |
534 | + return openstack_codenames[vers] |
535 | + except KeyError: |
536 | + e = 'Could not determine OpenStack codename for version %s' % vers |
537 | + error_out(e) |
538 | + |
539 | + |
540 | +def get_os_version_codename(codename): |
541 | + '''Determine OpenStack version number from codename.''' |
542 | + for k, v in openstack_codenames.iteritems(): |
543 | + if v == codename: |
544 | + return k |
545 | + e = 'Code not derive OpenStack version for '\ |
546 | + 'codename: %s' % codename |
547 | + error_out(e) |
548 | + |
549 | + |
550 | +def get_os_codename_package(pkg): |
551 | + '''Derive OpenStack release codename from an installed package.''' |
552 | + apt.init() |
553 | + cache = apt.Cache() |
554 | + try: |
555 | + pkg = cache[pkg] |
556 | + except: |
557 | + e = 'Could not determine version of installed package: %s' % pkg |
558 | + error_out(e) |
559 | + |
560 | + vers = apt.UpstreamVersion(pkg.current_ver.ver_str) |
561 | + |
562 | + try: |
563 | + if 'swift' in pkg.name: |
564 | + vers = vers[:5] |
565 | + return swift_codenames[vers] |
566 | + else: |
567 | + vers = vers[:6] |
568 | + return openstack_codenames[vers] |
569 | + except KeyError: |
570 | + e = 'Could not determine OpenStack codename for version %s' % vers |
571 | + error_out(e) |
572 | + |
573 | + |
574 | +def get_os_version_package(pkg): |
575 | + '''Derive OpenStack version number from an installed package.''' |
576 | + codename = get_os_codename_package(pkg) |
577 | + |
578 | + if 'swift' in pkg: |
579 | + vers_map = swift_codenames |
580 | + else: |
581 | + vers_map = openstack_codenames |
582 | + |
583 | + for version, cname in vers_map.iteritems(): |
584 | + if cname == codename: |
585 | + return version |
586 | + e = "Could not determine OpenStack version for package: %s" % pkg |
587 | + error_out(e) |
588 | + |
589 | + |
590 | +def configure_installation_source(rel): |
591 | + '''Configure apt installation source.''' |
592 | + |
593 | + def _import_key(keyid): |
594 | + cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \ |
595 | + "--recv-keys %s" % keyid |
596 | + try: |
597 | + subprocess.check_call(cmd.split(' ')) |
598 | + except subprocess.CalledProcessError: |
599 | + error_out("Error importing repo key %s" % keyid) |
600 | + |
601 | + if rel == 'distro': |
602 | + return |
603 | + elif rel[:4] == "ppa:": |
604 | + src = rel |
605 | + subprocess.check_call(["add-apt-repository", "-y", src]) |
606 | + elif rel[:3] == "deb": |
607 | + l = len(rel.split('|')) |
608 | + if l == 2: |
609 | + src, key = rel.split('|') |
610 | + juju_log("Importing PPA key from keyserver for %s" % src) |
611 | + _import_key(key) |
612 | + elif l == 1: |
613 | + src = rel |
614 | + else: |
615 | + error_out("Invalid openstack-release: %s" % rel) |
616 | + |
617 | + with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f: |
618 | + f.write(src) |
619 | + elif rel[:6] == 'cloud:': |
620 | + ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] |
621 | + rel = rel.split(':')[1] |
622 | + u_rel = rel.split('-')[0] |
623 | + ca_rel = rel.split('-')[1] |
624 | + |
625 | + if u_rel != ubuntu_rel: |
626 | + e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\ |
627 | + 'version (%s)' % (ca_rel, ubuntu_rel) |
628 | + error_out(e) |
629 | + |
630 | + if 'staging' in ca_rel: |
631 | + # staging is just a regular PPA. |
632 | + os_rel = ca_rel.split('/')[0] |
633 | + ppa = 'ppa:ubuntu-cloud-archive/%s-staging' % os_rel |
634 | + cmd = 'add-apt-repository -y %s' % ppa |
635 | + subprocess.check_call(cmd.split(' ')) |
636 | + return |
637 | + |
638 | + # map charm config options to actual archive pockets. |
639 | + pockets = { |
640 | + 'folsom': 'precise-updates/folsom', |
641 | + 'folsom/updates': 'precise-updates/folsom', |
642 | + 'folsom/proposed': 'precise-proposed/folsom', |
643 | + 'grizzly': 'precise-updates/grizzly', |
644 | + 'grizzly/updates': 'precise-updates/grizzly', |
645 | + 'grizzly/proposed': 'precise-proposed/grizzly' |
646 | + } |
647 | + |
648 | + try: |
649 | + pocket = pockets[ca_rel] |
650 | + except KeyError: |
651 | + e = 'Invalid Cloud Archive release specified: %s' % rel |
652 | + error_out(e) |
653 | + |
654 | + src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket) |
655 | + _import_key(CLOUD_ARCHIVE_KEY_ID) |
656 | + |
657 | + with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f: |
658 | + f.write(src) |
659 | + else: |
660 | + error_out("Invalid openstack-release specified: %s" % rel) |
661 | + |
662 | + |
663 | +def save_script_rc(script_path="scripts/scriptrc", **env_vars): |
664 | + """ |
665 | + Write an rc file in the charm-delivered directory containing |
666 | + exported environment variables provided by env_vars. Any charm scripts run |
667 | + outside the juju hook environment can source this scriptrc to obtain |
668 | + updated config information necessary to perform health checks or |
669 | + service changes. |
670 | + """ |
671 | + charm_dir = os.getenv('CHARM_DIR') |
672 | + juju_rc_path = "%s/%s" % (charm_dir, script_path) |
673 | + with open(juju_rc_path, 'wb') as rc_script: |
674 | + rc_script.write( |
675 | + "#!/bin/bash\n") |
676 | + [rc_script.write('export %s=%s\n' % (u, p)) |
677 | + for u, p in env_vars.iteritems() if u != "script_path"] |
678 | |
679 | === added file 'hooks/lib/utils.py' |
680 | --- hooks/lib/utils.py 1970-01-01 00:00:00 +0000 |
681 | +++ hooks/lib/utils.py 2013-05-29 17:55:39 +0000 |
682 | @@ -0,0 +1,359 @@ |
683 | +# |
684 | +# Copyright 2012 Canonical Ltd. |
685 | +# |
686 | +# This file is sourced from lp:openstack-charm-helpers |
687 | +# |
688 | +# Authors: |
689 | +# James Page <james.page@ubuntu.com> |
690 | +# Paul Collins <paul.collins@canonical.com> |
691 | +# Adam Gandelman <adamg@ubuntu.com> |
692 | +# |
693 | + |
694 | +import json |
695 | +import os |
696 | +import subprocess |
697 | +import socket |
698 | +import sys |
699 | +import hashlib |
700 | + |
701 | + |
702 | +def do_hooks(hooks): |
703 | + hook = os.path.basename(sys.argv[0]) |
704 | + |
705 | + try: |
706 | + hook_func = hooks[hook] |
707 | + except KeyError: |
708 | + juju_log('INFO', |
709 | + "This charm doesn't know how to handle '{}'.".format(hook)) |
710 | + else: |
711 | + hook_func() |
712 | + |
713 | + |
714 | +def install(*pkgs): |
715 | + cmd = [ |
716 | + 'apt-get', |
717 | + '-y', |
718 | + 'install' |
719 | + ] |
720 | + for pkg in pkgs: |
721 | + cmd.append(pkg) |
722 | + subprocess.check_call(cmd) |
723 | + |
724 | +TEMPLATES_DIR = 'templates' |
725 | + |
726 | +try: |
727 | + import jinja2 |
728 | +except ImportError: |
729 | + install('python-jinja2') |
730 | + import jinja2 |
731 | + |
732 | +try: |
733 | + import dns.resolver |
734 | +except ImportError: |
735 | + install('python-dnspython') |
736 | + import dns.resolver |
737 | + |
738 | + |
739 | +def render_template(template_name, context, template_dir=TEMPLATES_DIR): |
740 | + templates = jinja2.Environment( |
741 | + loader=jinja2.FileSystemLoader(template_dir) |
742 | + ) |
743 | + template = templates.get_template(template_name) |
744 | + return template.render(context) |
745 | + |
746 | +CLOUD_ARCHIVE = \ |
747 | +""" # Ubuntu Cloud Archive |
748 | +deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
749 | +""" |
750 | + |
751 | +CLOUD_ARCHIVE_POCKETS = { |
752 | + 'folsom': 'precise-updates/folsom', |
753 | + 'folsom/updates': 'precise-updates/folsom', |
754 | + 'folsom/proposed': 'precise-proposed/folsom', |
755 | + 'grizzly': 'precise-updates/grizzly', |
756 | + 'grizzly/updates': 'precise-updates/grizzly', |
757 | + 'grizzly/proposed': 'precise-proposed/grizzly' |
758 | + } |
759 | + |
760 | + |
761 | +def configure_source(): |
762 | + source = str(config_get('openstack-origin')) |
763 | + if not source: |
764 | + return |
765 | + if source.startswith('ppa:'): |
766 | + cmd = [ |
767 | + 'add-apt-repository', |
768 | + source |
769 | + ] |
770 | + subprocess.check_call(cmd) |
771 | + if source.startswith('cloud:'): |
772 | + # CA values should be formatted as cloud:ubuntu-openstack/pocket, eg: |
773 | + # cloud:precise-folsom/updates or cloud:precise-folsom/proposed |
774 | + install('ubuntu-cloud-keyring') |
775 | + pocket = source.split(':')[1] |
776 | + pocket = pocket.split('-')[1] |
777 | + with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: |
778 | + apt.write(CLOUD_ARCHIVE.format(CLOUD_ARCHIVE_POCKETS[pocket])) |
779 | + if source.startswith('deb'): |
780 | + l = len(source.split('|')) |
781 | + if l == 2: |
782 | + (apt_line, key) = source.split('|') |
783 | + cmd = [ |
784 | + 'apt-key', |
785 | + 'adv', '--keyserver keyserver.ubuntu.com', |
786 | + '--recv-keys', key |
787 | + ] |
788 | + subprocess.check_call(cmd) |
789 | + elif l == 1: |
790 | + apt_line = source |
791 | + |
792 | + with open('/etc/apt/sources.list.d/quantum.list', 'w') as apt: |
793 | + apt.write(apt_line + "\n") |
794 | + cmd = [ |
795 | + 'apt-get', |
796 | + 'update' |
797 | + ] |
798 | + subprocess.check_call(cmd) |
799 | + |
800 | +# Protocols |
801 | +TCP = 'TCP' |
802 | +UDP = 'UDP' |
803 | + |
804 | + |
805 | +def expose(port, protocol='TCP'): |
806 | + cmd = [ |
807 | + 'open-port', |
808 | + '{}/{}'.format(port, protocol) |
809 | + ] |
810 | + subprocess.check_call(cmd) |
811 | + |
812 | + |
813 | +def juju_log(severity, message): |
814 | + cmd = [ |
815 | + 'juju-log', |
816 | + '--log-level', severity, |
817 | + message |
818 | + ] |
819 | + subprocess.check_call(cmd) |
820 | + |
821 | + |
822 | +cache = {} |
823 | + |
824 | + |
825 | +def cached(func): |
826 | + def wrapper(*args, **kwargs): |
827 | + global cache |
828 | + key = str((func, args, kwargs)) |
829 | + try: |
830 | + return cache[key] |
831 | + except KeyError: |
832 | + res = func(*args, **kwargs) |
833 | + cache[key] = res |
834 | + return res |
835 | + return wrapper |
836 | + |
837 | + |
838 | +@cached |
839 | +def relation_ids(relation): |
840 | + cmd = [ |
841 | + 'relation-ids', |
842 | + relation |
843 | + ] |
844 | + result = str(subprocess.check_output(cmd)).split() |
845 | + if result == "": |
846 | + return None |
847 | + else: |
848 | + return result |
849 | + |
850 | + |
851 | +@cached |
852 | +def relation_list(rid): |
853 | + cmd = [ |
854 | + 'relation-list', |
855 | + '-r', rid, |
856 | + ] |
857 | + result = str(subprocess.check_output(cmd)).split() |
858 | + if result == "": |
859 | + return None |
860 | + else: |
861 | + return result |
862 | + |
863 | + |
864 | +@cached |
865 | +def relation_get(attribute, unit=None, rid=None): |
866 | + cmd = [ |
867 | + 'relation-get', |
868 | + ] |
869 | + if rid: |
870 | + cmd.append('-r') |
871 | + cmd.append(rid) |
872 | + cmd.append(attribute) |
873 | + if unit: |
874 | + cmd.append(unit) |
875 | + value = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
876 | + if value == "": |
877 | + return None |
878 | + else: |
879 | + return value |
880 | + |
881 | + |
882 | +@cached |
883 | +def relation_get_dict(relation_id=None, remote_unit=None): |
884 | + """Obtain all relation data as dict by way of JSON""" |
885 | + cmd = [ |
886 | + 'relation-get', '--format=json' |
887 | + ] |
888 | + if relation_id: |
889 | + cmd.append('-r') |
890 | + cmd.append(relation_id) |
891 | + if remote_unit: |
892 | + remote_unit_orig = os.getenv('JUJU_REMOTE_UNIT', None) |
893 | + os.environ['JUJU_REMOTE_UNIT'] = remote_unit |
894 | + j = subprocess.check_output(cmd) |
895 | + if remote_unit and remote_unit_orig: |
896 | + os.environ['JUJU_REMOTE_UNIT'] = remote_unit_orig |
897 | + d = json.loads(j) |
898 | + settings = {} |
899 | + # convert unicode to strings |
900 | + for k, v in d.iteritems(): |
901 | + settings[str(k)] = str(v) |
902 | + return settings |
903 | + |
904 | + |
905 | +def relation_set(**kwargs): |
906 | + cmd = [ |
907 | + 'relation-set' |
908 | + ] |
909 | + args = [] |
910 | + for k, v in kwargs.items(): |
911 | + if k == 'rid': |
912 | + if v: |
913 | + cmd.append('-r') |
914 | + cmd.append(v) |
915 | + else: |
916 | + args.append('{}={}'.format(k, v)) |
917 | + cmd += args |
918 | + subprocess.check_call(cmd) |
919 | + |
920 | + |
921 | +@cached |
922 | +def unit_get(attribute): |
923 | + cmd = [ |
924 | + 'unit-get', |
925 | + attribute |
926 | + ] |
927 | + value = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
928 | + if value == "": |
929 | + return None |
930 | + else: |
931 | + return value |
932 | + |
933 | + |
934 | +@cached |
935 | +def config_get(attribute): |
936 | + cmd = [ |
937 | + 'config-get', |
938 | + '--format', |
939 | + 'json', |
940 | + ] |
941 | + out = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
942 | + cfg = json.loads(out) |
943 | + |
944 | + try: |
945 | + return cfg[attribute] |
946 | + except KeyError: |
947 | + return None |
948 | + |
949 | + |
950 | +@cached |
951 | +def get_unit_hostname(): |
952 | + return socket.gethostname() |
953 | + |
954 | + |
955 | +@cached |
956 | +def get_host_ip(hostname=unit_get('private-address')): |
957 | + try: |
958 | + # Test to see if already an IPv4 address |
959 | + socket.inet_aton(hostname) |
960 | + return hostname |
961 | + except socket.error: |
962 | + answers = dns.resolver.query(hostname, 'A') |
963 | + if answers: |
964 | + return answers[0].address |
965 | + return None |
966 | + |
967 | + |
968 | +def _svc_control(service, action): |
969 | + subprocess.check_call(['service', service, action]) |
970 | + |
971 | + |
972 | +def restart(*services): |
973 | + for service in services: |
974 | + _svc_control(service, 'restart') |
975 | + |
976 | + |
977 | +def stop(*services): |
978 | + for service in services: |
979 | + _svc_control(service, 'stop') |
980 | + |
981 | + |
982 | +def start(*services): |
983 | + for service in services: |
984 | + _svc_control(service, 'start') |
985 | + |
986 | + |
987 | +def reload(*services): |
988 | + for service in services: |
989 | + try: |
990 | + _svc_control(service, 'reload') |
991 | + except subprocess.CalledProcessError: |
992 | + # Reload failed - either service does not support reload |
993 | + # or it was not running - restart will fixup most things |
994 | + _svc_control(service, 'restart') |
995 | + |
996 | + |
997 | +def running(service): |
998 | + try: |
999 | + output = subprocess.check_output(['service', service, 'status']) |
1000 | + except subprocess.CalledProcessError: |
1001 | + return False |
1002 | + else: |
1003 | + if ("start/running" in output or |
1004 | + "is running" in output): |
1005 | + return True |
1006 | + else: |
1007 | + return False |
1008 | + |
1009 | + |
1010 | +def file_hash(path): |
1011 | + if os.path.exists(path): |
1012 | + h = hashlib.md5() |
1013 | + with open(path, 'r') as source: |
1014 | + h.update(source.read()) # IGNORE:E1101 - it does have update |
1015 | + return h.hexdigest() |
1016 | + else: |
1017 | + return None |
1018 | + |
1019 | + |
1020 | +def inteli_restart(restart_map): |
1021 | + def wrap(f): |
1022 | + def wrapped_f(*args): |
1023 | + checksums = {} |
1024 | + for path in restart_map: |
1025 | + checksums[path] = file_hash(path) |
1026 | + f(*args) |
1027 | + restarts = [] |
1028 | + for path in restart_map: |
1029 | + if checksums[path] != file_hash(path): |
1030 | + restarts += restart_map[path] |
1031 | + restart(*list(set(restarts))) |
1032 | + return wrapped_f |
1033 | + return wrap |
1034 | + |
1035 | + |
1036 | +def is_relation_made(relation, key='private-address'): |
1037 | + for r_id in (relation_ids(relation) or []): |
1038 | + for unit in (relation_list(r_id) or []): |
1039 | + if relation_get(key, rid=r_id, unit=unit): |
1040 | + return True |
1041 | + return False |
1042 | |
1043 | === modified file 'hooks/quantum_utils.py' |
1044 | --- hooks/quantum_utils.py 2012-12-03 15:16:55 +0000 |
1045 | +++ hooks/quantum_utils.py 2013-05-29 17:55:39 +0000 |
1046 | @@ -1,26 +1,27 @@ |
1047 | import subprocess |
1048 | -from utils import juju_log as log |
1049 | +import os |
1050 | +import uuid |
1051 | +import base64 |
1052 | +import apt_pkg as apt |
1053 | +from lib.utils import ( |
1054 | + juju_log as log, |
1055 | + configure_source, |
1056 | + config_get |
1057 | + ) |
1058 | |
1059 | |
1060 | OVS = "ovs" |
1061 | -NVP = "nvp" |
1062 | |
1063 | OVS_PLUGIN = \ |
1064 | "quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2" |
1065 | -NVP_PLUGIN = \ |
1066 | - "quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2" |
1067 | CORE_PLUGIN = { |
1068 | OVS: OVS_PLUGIN, |
1069 | - NVP: NVP_PLUGIN |
1070 | } |
1071 | |
1072 | OVS_PLUGIN_CONF = \ |
1073 | "/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini" |
1074 | -NVP_PLUGIN_CONF = \ |
1075 | - "/etc/quantum/plugins/nicira/nvp.ini" |
1076 | PLUGIN_CONF = { |
1077 | OVS: OVS_PLUGIN_CONF, |
1078 | - NVP: NVP_PLUGIN_CONF |
1079 | } |
1080 | |
1081 | GATEWAY_PKGS = { |
1082 | @@ -28,28 +29,72 @@ |
1083 | "quantum-plugin-openvswitch-agent", |
1084 | "quantum-l3-agent", |
1085 | "quantum-dhcp-agent", |
1086 | - 'python-mysqldb' |
1087 | + 'python-mysqldb', |
1088 | + "nova-api-metadata" |
1089 | ], |
1090 | - NVP: [ |
1091 | - "quantum-plugin-nicira" |
1092 | - ] |
1093 | } |
1094 | |
1095 | GATEWAY_AGENTS = { |
1096 | OVS: [ |
1097 | "quantum-plugin-openvswitch-agent", |
1098 | "quantum-l3-agent", |
1099 | - "quantum-dhcp-agent" |
1100 | - ] |
1101 | + "quantum-dhcp-agent", |
1102 | + "nova-api-metadata" |
1103 | + ], |
1104 | } |
1105 | |
1106 | +EXT_PORT_CONF = '/etc/init/ext-port.conf' |
1107 | + |
1108 | + |
1109 | +def get_os_version(package=None): |
1110 | + apt.init() |
1111 | + cache = apt.Cache() |
1112 | + pkg = cache[package or 'quantum-common'] |
1113 | + if pkg.current_ver: |
1114 | + return apt.upstream_version(pkg.current_ver.ver_str) |
1115 | + else: |
1116 | + return None |
1117 | + |
1118 | + |
1119 | +if get_os_version('quantum-common') >= "2013.1": |
1120 | + for plugin in GATEWAY_AGENTS: |
1121 | + GATEWAY_AGENTS[plugin].append("quantum-metadata-agent") |
1122 | + |
1123 | DB_USER = "quantum" |
1124 | QUANTUM_DB = "quantum" |
1125 | KEYSTONE_SERVICE = "quantum" |
1126 | +NOVA_DB_USER = "nova" |
1127 | +NOVA_DB = "nova" |
1128 | |
1129 | QUANTUM_CONF = "/etc/quantum/quantum.conf" |
1130 | L3_AGENT_CONF = "/etc/quantum/l3_agent.ini" |
1131 | DHCP_AGENT_CONF = "/etc/quantum/dhcp_agent.ini" |
1132 | +METADATA_AGENT_CONF = "/etc/quantum/metadata_agent.ini" |
1133 | +NOVA_CONF = "/etc/nova/nova.conf" |
1134 | + |
1135 | +RESTART_MAP = { |
1136 | + QUANTUM_CONF: [ |
1137 | + 'quantum-l3-agent', |
1138 | + 'quantum-dhcp-agent', |
1139 | + 'quantum-metadata-agent', |
1140 | + 'quantum-plugin-openvswitch-agent' |
1141 | + ], |
1142 | + DHCP_AGENT_CONF: [ |
1143 | + 'quantum-dhcp-agent' |
1144 | + ], |
1145 | + L3_AGENT_CONF: [ |
1146 | + 'quantum-l3-agent' |
1147 | + ], |
1148 | + METADATA_AGENT_CONF: [ |
1149 | + 'quantum-metadata-agent' |
1150 | + ], |
1151 | + OVS_PLUGIN_CONF: [ |
1152 | + 'quantum-plugin-openvswitch-agent' |
1153 | + ], |
1154 | + NOVA_CONF: [ |
1155 | + 'nova-api-metadata' |
1156 | + ] |
1157 | + } |
1158 | |
1159 | RABBIT_USER = "nova" |
1160 | RABBIT_VHOST = "nova" |
1161 | @@ -90,3 +135,115 @@ |
1162 | 'Deleting port {} from bridge {}'.format(port, name)) |
1163 | subprocess.check_call(["ovs-vsctl", "del-port", name, port]) |
1164 | subprocess.check_call(["ip", "link", "set", port, "down"]) |
1165 | + |
1166 | + |
1167 | +SHARED_SECRET = "/etc/quantum/secret.txt" |
1168 | + |
1169 | + |
1170 | +def get_shared_secret(): |
1171 | + secret = None |
1172 | + if not os.path.exists(SHARED_SECRET): |
1173 | + secret = str(uuid.uuid4()) |
1174 | + with open(SHARED_SECRET, 'w') as secret_file: |
1175 | + secret_file.write(secret) |
1176 | + else: |
1177 | + with open(SHARED_SECRET, 'r') as secret_file: |
1178 | + secret = secret_file.read().strip() |
1179 | + return secret |
1180 | + |
1181 | + |
1182 | +def flush_local_configuration(): |
1183 | + if os.path.exists('/usr/bin/quantum-netns-cleanup'): |
1184 | + cmd = [ |
1185 | + "quantum-netns-cleanup", |
1186 | + "--config-file=/etc/quantum/quantum.conf" |
1187 | + ] |
1188 | + for agent_conf in ['l3_agent.ini', 'dhcp_agent.ini']: |
1189 | + agent_cmd = list(cmd) |
1190 | + agent_cmd.append('--config-file=/etc/quantum/{}'\ |
1191 | + .format(agent_conf)) |
1192 | + subprocess.call(agent_cmd) |
1193 | + |
1194 | + |
1195 | +def install_ca(ca_cert): |
1196 | + with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt', |
1197 | + 'w') as crt: |
1198 | + crt.write(base64.b64decode(ca_cert)) |
1199 | + subprocess.check_call(['update-ca-certificates', '--fresh']) |
1200 | + |
1201 | +DHCP_AGENT = "DHCP Agent" |
1202 | +L3_AGENT = "L3 Agent" |
1203 | + |
1204 | + |
1205 | +def reassign_agent_resources(env): |
1206 | + ''' Use agent scheduler API to detect down agents and re-schedule ''' |
1207 | + from quantumclient.v2_0 import client |
1208 | + # TODO: Fixup for https keystone |
1209 | + auth_url = 'http://%(keystone_host)s:%(auth_port)s/v2.0' % env |
1210 | + quantum = client.Client(username=env['service_username'], |
1211 | + password=env['service_password'], |
1212 | + tenant_name=env['service_tenant'], |
1213 | + auth_url=auth_url, |
1214 | + region_name=env['region']) |
1215 | + |
1216 | + agents = quantum.list_agents(agent_type=DHCP_AGENT) |
1217 | + dhcp_agents = [] |
1218 | + l3_agents = [] |
1219 | + networks = {} |
1220 | + for agent in agents['agents']: |
1221 | + if not agent['alive']: |
1222 | + log('INFO', 'DHCP Agent %s down' % agent['id']) |
1223 | + for network in \ |
1224 | + quantum.list_networks_on_dhcp_agent(agent['id'])['networks']: |
1225 | + networks[network['id']] = agent['id'] |
1226 | + else: |
1227 | + dhcp_agents.append(agent['id']) |
1228 | + |
1229 | + agents = quantum.list_agents(agent_type=L3_AGENT) |
1230 | + routers = {} |
1231 | + for agent in agents['agents']: |
1232 | + if not agent['alive']: |
1233 | + log('INFO', 'L3 Agent %s down' % agent['id']) |
1234 | + for router in \ |
1235 | + quantum.list_routers_on_l3_agent(agent['id'])['routers']: |
1236 | + routers[router['id']] = agent['id'] |
1237 | + else: |
1238 | + l3_agents.append(agent['id']) |
1239 | + |
1240 | + index = 0 |
1241 | + for router_id in routers: |
1242 | + agent = index % len(l3_agents) |
1243 | + log('INFO', |
1244 | + 'Moving router %s from %s to %s' % \ |
1245 | + (router_id, routers[router_id], l3_agents[agent])) |
1246 | + quantum.remove_router_from_l3_agent(l3_agent=routers[router_id], |
1247 | + router_id=router_id) |
1248 | + quantum.add_router_to_l3_agent(l3_agent=l3_agents[agent], |
1249 | + body={'router_id': router_id}) |
1250 | + index += 1 |
1251 | + |
1252 | + index = 0 |
1253 | + for network_id in networks: |
1254 | + agent = index % len(dhcp_agents) |
1255 | + log('INFO', |
1256 | + 'Moving network %s from %s to %s' % \ |
1257 | + (network_id, networks[network_id], dhcp_agents[agent])) |
1258 | + quantum.remove_network_from_dhcp_agent(dhcp_agent=networks[network_id], |
1259 | + network_id=network_id) |
1260 | + quantum.add_network_to_dhcp_agent(dhcp_agent=dhcp_agents[agent], |
1261 | + body={'network_id': network_id}) |
1262 | + index += 1 |
1263 | + |
1264 | +def do_openstack_upgrade(): |
1265 | + configure_source() |
1266 | + plugin = config_get('plugin') |
1267 | + pkgs = [] |
1268 | + if plugin in GATEWAY_PKGS.keys(): |
1269 | + pkgs += GATEWAY_PKGS[plugin] |
1270 | + if plugin == OVS: |
1271 | + pkgs.append('openvswitch-datapath-dkms') |
1272 | + cmd = ['apt-get', '-y', |
1273 | + '--option', 'Dpkg::Options::=--force-confold', |
1274 | + '--option', 'Dpkg::Options::=--force-confdef', |
1275 | + 'install'] + pkgs |
1276 | + subprocess.check_call(cmd) |
1277 | |
1278 | === removed file 'hooks/utils.py' |
1279 | --- hooks/utils.py 2012-12-07 08:36:54 +0000 |
1280 | +++ hooks/utils.py 1970-01-01 00:00:00 +0000 |
1281 | @@ -1,237 +0,0 @@ |
1282 | - |
1283 | -# |
1284 | -# Copyright 2012 Canonical Ltd. |
1285 | -# |
1286 | -# Authors: |
1287 | -# James Page <james.page@ubuntu.com> |
1288 | -# Paul Collins <paul.collins@canonical.com> |
1289 | -# |
1290 | - |
1291 | -import os |
1292 | -import subprocess |
1293 | -import socket |
1294 | -import sys |
1295 | - |
1296 | - |
1297 | -def do_hooks(hooks): |
1298 | - hook = os.path.basename(sys.argv[0]) |
1299 | - |
1300 | - try: |
1301 | - hook_func = hooks[hook] |
1302 | - except KeyError: |
1303 | - juju_log('INFO', |
1304 | - "This charm doesn't know how to handle '{}'.".format(hook)) |
1305 | - else: |
1306 | - hook_func() |
1307 | - |
1308 | - |
1309 | -def install(*pkgs): |
1310 | - cmd = [ |
1311 | - 'apt-get', |
1312 | - '-y', |
1313 | - 'install' |
1314 | - ] |
1315 | - for pkg in pkgs: |
1316 | - cmd.append(pkg) |
1317 | - subprocess.check_call(cmd) |
1318 | - |
1319 | -TEMPLATES_DIR = 'templates' |
1320 | - |
1321 | -try: |
1322 | - import jinja2 |
1323 | -except ImportError: |
1324 | - install('python-jinja2') |
1325 | - import jinja2 |
1326 | - |
1327 | -try: |
1328 | - import dns.resolver |
1329 | -except ImportError: |
1330 | - install('python-dnspython') |
1331 | - import dns.resolver |
1332 | - |
1333 | - |
1334 | -def render_template(template_name, context, template_dir=TEMPLATES_DIR): |
1335 | - templates = jinja2.Environment( |
1336 | - loader=jinja2.FileSystemLoader(template_dir) |
1337 | - ) |
1338 | - template = templates.get_template(template_name) |
1339 | - return template.render(context) |
1340 | - |
1341 | -CLOUD_ARCHIVE = \ |
1342 | -""" # Ubuntu Cloud Archive |
1343 | -deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main |
1344 | -""" |
1345 | - |
1346 | -CLOUD_ARCHIVE_POCKETS = { |
1347 | - 'precise-folsom': 'precise-updates/folsom', |
1348 | - 'precise-folsom/updates': 'precise-updates/folsom', |
1349 | - 'precise-folsom/proposed': 'precise-proposed/folsom', |
1350 | - 'precise-grizzly': 'precise-updates/grizzly', |
1351 | - 'precise-grizzly/updates': 'precise-updates/grizzly', |
1352 | - 'precise-grizzly/proposed': 'precise-proposed/grizzly' |
1353 | - } |
1354 | - |
1355 | - |
1356 | -def configure_source(): |
1357 | - source = str(config_get('openstack-origin')) |
1358 | - if not source: |
1359 | - return |
1360 | - if source.startswith('ppa:'): |
1361 | - cmd = [ |
1362 | - 'add-apt-repository', |
1363 | - source |
1364 | - ] |
1365 | - subprocess.check_call(cmd) |
1366 | - if source.startswith('cloud:'): |
1367 | - install('ubuntu-cloud-keyring') |
1368 | - pocket = source.split(':')[1] |
1369 | - with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: |
1370 | - apt.write(CLOUD_ARCHIVE.format(CLOUD_ARCHIVE_POCKETS[pocket])) |
1371 | - if source.startswith('deb'): |
1372 | - l = len(source.split('|')) |
1373 | - if l == 2: |
1374 | - (apt_line, key) = source.split('|') |
1375 | - cmd = [ |
1376 | - 'apt-key', |
1377 | - 'adv', '--keyserver keyserver.ubuntu.com', |
1378 | - '--recv-keys', key |
1379 | - ] |
1380 | - subprocess.check_call(cmd) |
1381 | - elif l == 1: |
1382 | - apt_line = source |
1383 | - |
1384 | - with open('/etc/apt/sources.list.d/quantum.list', 'w') as apt: |
1385 | - apt.write(apt_line + "\n") |
1386 | - cmd = [ |
1387 | - 'apt-get', |
1388 | - 'update' |
1389 | - ] |
1390 | - subprocess.check_call(cmd) |
1391 | - |
1392 | -# Protocols |
1393 | -TCP = 'TCP' |
1394 | -UDP = 'UDP' |
1395 | - |
1396 | - |
1397 | -def expose(port, protocol='TCP'): |
1398 | - cmd = [ |
1399 | - 'open-port', |
1400 | - '{}/{}'.format(port, protocol) |
1401 | - ] |
1402 | - subprocess.check_call(cmd) |
1403 | - |
1404 | - |
1405 | -def juju_log(severity, message): |
1406 | - cmd = [ |
1407 | - 'juju-log', |
1408 | - '--log-level', severity, |
1409 | - message |
1410 | - ] |
1411 | - subprocess.check_call(cmd) |
1412 | - |
1413 | - |
1414 | -def relation_ids(relation): |
1415 | - cmd = [ |
1416 | - 'relation-ids', |
1417 | - relation |
1418 | - ] |
1419 | - return subprocess.check_output(cmd).split() # IGNORE:E1103 |
1420 | - |
1421 | - |
1422 | -def relation_list(rid): |
1423 | - cmd = [ |
1424 | - 'relation-list', |
1425 | - '-r', rid, |
1426 | - ] |
1427 | - return subprocess.check_output(cmd).split() # IGNORE:E1103 |
1428 | - |
1429 | - |
1430 | -def relation_get(attribute, unit=None, rid=None): |
1431 | - cmd = [ |
1432 | - 'relation-get', |
1433 | - ] |
1434 | - if rid: |
1435 | - cmd.append('-r') |
1436 | - cmd.append(rid) |
1437 | - cmd.append(attribute) |
1438 | - if unit: |
1439 | - cmd.append(unit) |
1440 | - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
1441 | - if value == "": |
1442 | - return None |
1443 | - else: |
1444 | - return value |
1445 | - |
1446 | - |
1447 | -def relation_set(**kwargs): |
1448 | - cmd = [ |
1449 | - 'relation-set' |
1450 | - ] |
1451 | - args = [] |
1452 | - for k, v in kwargs.items(): |
1453 | - if k == 'rid': |
1454 | - cmd.append('-r') |
1455 | - cmd.append(v) |
1456 | - else: |
1457 | - args.append('{}={}'.format(k, v)) |
1458 | - cmd += args |
1459 | - subprocess.check_call(cmd) |
1460 | - |
1461 | - |
1462 | -def unit_get(attribute): |
1463 | - cmd = [ |
1464 | - 'unit-get', |
1465 | - attribute |
1466 | - ] |
1467 | - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
1468 | - if value == "": |
1469 | - return None |
1470 | - else: |
1471 | - return value |
1472 | - |
1473 | - |
1474 | -def config_get(attribute): |
1475 | - cmd = [ |
1476 | - 'config-get', |
1477 | - attribute |
1478 | - ] |
1479 | - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 |
1480 | - if value == "": |
1481 | - return None |
1482 | - else: |
1483 | - return value |
1484 | - |
1485 | - |
1486 | -def get_unit_hostname(): |
1487 | - return socket.gethostname() |
1488 | - |
1489 | - |
1490 | -def get_host_ip(hostname=unit_get('private-address')): |
1491 | - try: |
1492 | - # Test to see if already an IPv4 address |
1493 | - socket.inet_aton(hostname) |
1494 | - return hostname |
1495 | - except socket.error: |
1496 | - pass |
1497 | - try: |
1498 | - answers = dns.resolver.query(hostname, 'A') |
1499 | - if answers: |
1500 | - return answers[0].address |
1501 | - except dns.resolver.NXDOMAIN: |
1502 | - pass |
1503 | - return None |
1504 | - |
1505 | - |
1506 | -def restart(*services): |
1507 | - for service in services: |
1508 | - subprocess.check_call(['service', service, 'restart']) |
1509 | - |
1510 | - |
1511 | -def stop(*services): |
1512 | - for service in services: |
1513 | - subprocess.check_call(['service', service, 'stop']) |
1514 | - |
1515 | - |
1516 | -def start(*services): |
1517 | - for service in services: |
1518 | - subprocess.check_call(['service', service, 'start']) |
1519 | |
1520 | === modified file 'metadata.yaml' |
1521 | --- metadata.yaml 2012-12-03 15:16:55 +0000 |
1522 | +++ metadata.yaml 2013-05-29 17:55:39 +0000 |
1523 | @@ -20,4 +20,7 @@ |
1524 | shared-db: |
1525 | interface: mysql-shared |
1526 | amqp: |
1527 | - interface: rabbitmq |
1528 | \ No newline at end of file |
1529 | + interface: rabbitmq |
1530 | +peers: |
1531 | + cluster: |
1532 | + interface: quantum-gateway-ha |
1533 | \ No newline at end of file |
1534 | |
1535 | === modified file 'revision' |
1536 | --- revision 2012-12-07 08:36:54 +0000 |
1537 | +++ revision 2013-05-29 17:55:39 +0000 |
1538 | @@ -1,1 +1,1 @@ |
1539 | -36 |
1540 | +56 |
1541 | |
1542 | === added file 'templates/evacuate_unit.py' |
1543 | --- templates/evacuate_unit.py 1970-01-01 00:00:00 +0000 |
1544 | +++ templates/evacuate_unit.py 2013-05-29 17:55:39 +0000 |
1545 | @@ -0,0 +1,70 @@ |
1546 | +#!/usr/bin/python |
1547 | + |
1548 | +import subprocess |
1549 | + |
1550 | + |
1551 | +def log(priority, message): |
1552 | + print "{}: {}".format(priority, message) |
1553 | + |
1554 | +DHCP_AGENT = "DHCP Agent" |
1555 | +L3_AGENT = "L3 Agent" |
1556 | + |
1557 | + |
1558 | +def evacuate_unit(unit): |
1559 | + ''' Use agent scheduler API to detect down agents and re-schedule ''' |
1560 | + from quantumclient.v2_0 import client |
1561 | + # TODO: Fixup for https keystone |
1562 | + auth_url = 'http://{{ keystone_host }}:{{ auth_port }}/v2.0' |
1563 | + quantum = client.Client(username='{{ service_username }}', |
1564 | + password='{{ service_password }}', |
1565 | + tenant_name='{{ service_tenant }}', |
1566 | + auth_url=auth_url, |
1567 | + region_name='{{ region }}') |
1568 | + |
1569 | + agents = quantum.list_agents(agent_type=DHCP_AGENT) |
1570 | + dhcp_agents = [] |
1571 | + l3_agents = [] |
1572 | + networks = {} |
1573 | + for agent in agents['agents']: |
1574 | + if agent['alive'] and agent['host'] != unit: |
1575 | + dhcp_agents.append(agent['id']) |
1576 | + elif agent['host'] == unit: |
1577 | + for network in \ |
1578 | + quantum.list_networks_on_dhcp_agent(agent['id'])['networks']: |
1579 | + networks[network['id']] = agent['id'] |
1580 | + |
1581 | + agents = quantum.list_agents(agent_type=L3_AGENT) |
1582 | + routers = {} |
1583 | + for agent in agents['agents']: |
1584 | + if agent['alive'] and agent['host'] != unit: |
1585 | + l3_agents.append(agent['id']) |
1586 | + elif agent['host'] == unit: |
1587 | + for router in \ |
1588 | + quantum.list_routers_on_l3_agent(agent['id'])['routers']: |
1589 | + routers[router['id']] = agent['id'] |
1590 | + |
1591 | + index = 0 |
1592 | + for router_id in routers: |
1593 | + agent = index % len(l3_agents) |
1594 | + log('INFO', |
1595 | + 'Moving router %s from %s to %s' % \ |
1596 | + (router_id, routers[router_id], l3_agents[agent])) |
1597 | + quantum.remove_router_from_l3_agent(l3_agent=routers[router_id], |
1598 | + router_id=router_id) |
1599 | + quantum.add_router_to_l3_agent(l3_agent=l3_agents[agent], |
1600 | + body={'router_id': router_id}) |
1601 | + index += 1 |
1602 | + |
1603 | + index = 0 |
1604 | + for network_id in networks: |
1605 | + agent = index % len(dhcp_agents) |
1606 | + log('INFO', |
1607 | + 'Moving network %s from %s to %s' % \ |
1608 | + (network_id, networks[network_id], dhcp_agents[agent])) |
1609 | + quantum.remove_network_from_dhcp_agent(dhcp_agent=networks[network_id], |
1610 | + network_id=network_id) |
1611 | + quantum.add_network_to_dhcp_agent(dhcp_agent=dhcp_agents[agent], |
1612 | + body={'network_id': network_id}) |
1613 | + index += 1 |
1614 | + |
1615 | +evacuate_unit(subprocess.check_output(['hostname', '-f']).strip()) |
1616 | |
1617 | === added file 'templates/ext-port.conf' |
1618 | --- templates/ext-port.conf 1970-01-01 00:00:00 +0000 |
1619 | +++ templates/ext-port.conf 2013-05-29 17:55:39 +0000 |
1620 | @@ -0,0 +1,9 @@ |
1621 | +description "Enabling Quantum external networking port" |
1622 | + |
1623 | +start on runlevel [2345] |
1624 | + |
1625 | +task |
1626 | + |
1627 | +script |
1628 | + ip link set {{ ext_port }} up |
1629 | +end script |
1630 | \ No newline at end of file |
1631 | |
1632 | === modified file 'templates/l3_agent.ini' |
1633 | --- templates/l3_agent.ini 2012-11-05 11:59:27 +0000 |
1634 | +++ templates/l3_agent.ini 2013-05-29 17:55:39 +0000 |
1635 | @@ -1,7 +1,7 @@ |
1636 | [DEFAULT] |
1637 | interface_driver = quantum.agent.linux.interface.OVSInterfaceDriver |
1638 | auth_url = http://{{ keystone_host }}:{{ service_port }}/v2.0 |
1639 | -auth_region = RegionOne |
1640 | +auth_region = {{ region }} |
1641 | admin_tenant_name = {{ service_tenant }} |
1642 | admin_user = {{ service_username }} |
1643 | admin_password = {{ service_password }} |
1644 | |
1645 | === added file 'templates/metadata_agent.ini' |
1646 | --- templates/metadata_agent.ini 1970-01-01 00:00:00 +0000 |
1647 | +++ templates/metadata_agent.ini 2013-05-29 17:55:39 +0000 |
1648 | @@ -0,0 +1,17 @@ |
1649 | +[DEFAULT] |
1650 | +debug = True |
1651 | +auth_url = http://{{ keystone_host }}:{{ service_port }}/v2.0 |
1652 | +auth_region = {{ region }} |
1653 | +admin_tenant_name = {{ service_tenant }} |
1654 | +admin_user = {{ service_username }} |
1655 | +admin_password = {{ service_password }} |
1656 | +root_helper = sudo quantum-rootwrap /etc/quantum/rootwrap.conf |
1657 | +state_path = /var/lib/quantum |
1658 | +# Gateway runs a metadata API server locally |
1659 | +nova_metadata_ip = {{ local_ip }} |
1660 | +nova_metadata_port = 8775 |
1661 | +# When proxying metadata requests, Quantum signs the Instance-ID header with a |
1662 | +# shared secret to prevent spoofing. You may select any string for a secret, |
1663 | +# but it must match here and in the configuration used by the Nova Metadata |
1664 | +# Server. NOTE: Nova uses a different key: quantum_metadata_proxy_shared_secret |
1665 | +metadata_proxy_shared_secret = {{ shared_secret }} |
1666 | |
1667 | === added file 'templates/nova.conf' |
1668 | --- templates/nova.conf 1970-01-01 00:00:00 +0000 |
1669 | +++ templates/nova.conf 2013-05-29 17:55:39 +0000 |
1670 | @@ -0,0 +1,25 @@ |
1671 | +[DEFAULT] |
1672 | +logdir=/var/log/nova |
1673 | +state_path=/var/lib/nova |
1674 | +lock_path=/var/lock/nova |
1675 | +root_helper=sudo nova-rootwrap /etc/nova/rootwrap.conf |
1676 | +verbose=True |
1677 | +api_paste_config=/etc/nova/api-paste.ini |
1678 | +enabled_apis=metadata |
1679 | +multi_host=True |
1680 | +sql_connection=mysql://{{ user }}:{{ password }}@{{ host }}/{{ db }} |
1681 | +quantum_metadata_proxy_shared_secret={{ shared_secret }} |
1682 | +service_quantum_metadata_proxy=True |
1683 | +# Access to message bus |
1684 | +rabbit_userid={{ rabbit_userid }} |
1685 | +rabbit_virtual_host={{ rabbit_virtual_host }} |
1686 | +rabbit_host={{ rabbit_host }} |
1687 | +rabbit_password={{ rabbit_password }} |
1688 | +# Access to quantum API services |
1689 | +network_api_class=nova.network.quantumv2.api.API |
1690 | +quantum_auth_strategy=keystone |
1691 | +quantum_url={{ quantum_url }} |
1692 | +quantum_admin_tenant_name={{ service_tenant }} |
1693 | +quantum_admin_username={{ service_username }} |
1694 | +quantum_admin_password={{ service_password }} |
1695 | +quantum_admin_auth_url=http://{{ keystone_host }}:{{ service_port }}/v2.0 |
1696 | |
1697 | === modified file 'templates/ovs_quantum_plugin.ini' |
1698 | --- templates/ovs_quantum_plugin.ini 2012-11-05 11:59:27 +0000 |
1699 | +++ templates/ovs_quantum_plugin.ini 2013-05-29 17:55:39 +0000 |
1700 | @@ -7,5 +7,5 @@ |
1701 | enable_tunneling = True |
1702 | tunnel_id_ranges = 1:1000 |
1703 | [AGENT] |
1704 | -polling_interval = 2 |
1705 | +polling_interval = 10 |
1706 | root_helper = sudo /usr/bin/quantum-rootwrap /etc/quantum/rootwrap.conf |
1707 | |
1708 | === modified file 'templates/quantum.conf' |
1709 | --- templates/quantum.conf 2012-11-05 11:59:27 +0000 |
1710 | +++ templates/quantum.conf 2013-05-29 17:55:39 +0000 |
1711 | @@ -12,4 +12,9 @@ |
1712 | control_exchange = quantum |
1713 | notification_driver = quantum.openstack.common.notifier.list_notifier |
1714 | list_notifier_drivers = quantum.openstack.common.notifier.rabbit_notifier |
1715 | -[QUOTAS] |
1716 | +lock_path = /var/lock/quantum |
1717 | +# Ensure that netns cleanup operations kill processes and remove ports |
1718 | +# force = true |
1719 | +[AGENT] |
1720 | +root_helper = sudo /usr/bin/quantum-rootwrap /etc/quantum/rootwrap.conf |
1721 | +[QUOTAS] |
1722 | \ No newline at end of file |