Merge lp:~openstack-charmers/charms/precise/quantum-gateway/ha-support into lp:~charmers/charms/precise/quantum-gateway/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
Reviewer Review Type Date Requested Status
charmers Pending
Review via email: mp+166333@code.launchpad.net

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

Subscribers

People subscribed via source and target branches