70
def config_flags_parser(config_flags):
71
if config_flags.find('==') >= 0:
72
log("config_flags is not in expected format (key=value)",
75
# strip the following from each value.
77
# we strip any leading/trailing '=' or ' ' from the string then
79
split = config_flags.strip(' =').split('=')
82
for i in xrange(0, limit - 1):
85
vindex = next.rfind(',')
86
if (i == limit - 2) or (vindex < 0):
94
# if this not the first entry, expect an embedded key.
95
index = current.rfind(',')
97
log("invalid config value(s) at index %s" % (i),
100
key = current[index + 1:]
103
flags[key.strip(post_strippers)] = value.rstrip(post_strippers)
71
107
class OSContextGenerator(object):
78
114
class SharedDBContext(OSContextGenerator):
79
115
interfaces = ['shared-db']
81
def __init__(self, database=None, user=None, relation_prefix=None):
118
database=None, user=None, relation_prefix=None, ssl_dir=None):
83
120
Allows inspecting relation for settings prefixed with relation_prefix.
84
121
This is useful for parsing access for multiple databases returned via
105
143
for rid in relation_ids('shared-db'):
106
144
for unit in related_units(rid):
107
passwd = relation_get(password_setting, rid=rid, unit=unit)
145
rdata = relation_get(rid=rid, unit=unit)
109
'database_host': relation_get('db_host', rid=rid,
147
'database_host': rdata.get('db_host'),
111
148
'database': self.database,
112
149
'database_user': self.user,
113
'database_password': passwd,
115
if context_complete(ctxt):
150
'database_password': rdata.get(password_setting),
151
'database_type': 'mysql'
153
if context_complete(ctxt):
154
db_ssl(rdata, ctxt, self.ssl_dir)
159
class PostgresqlDBContext(OSContextGenerator):
160
interfaces = ['pgsql-db']
162
def __init__(self, database=None):
163
self.database = database
166
self.database = self.database or config('database')
167
if self.database is None:
168
log('Could not generate postgresql_db context. '
169
'Missing required charm config options. '
174
for rid in relation_ids(self.interfaces[0]):
175
for unit in related_units(rid):
177
'database_host': relation_get('host', rid=rid, unit=unit),
178
'database': self.database,
179
'database_user': relation_get('user', rid=rid, unit=unit),
180
'database_password': relation_get('password', rid=rid, unit=unit),
181
'database_type': 'postgresql',
183
if context_complete(ctxt):
188
def db_ssl(rdata, ctxt, ssl_dir):
189
if 'ssl_ca' in rdata and ssl_dir:
190
ca_path = os.path.join(ssl_dir, 'db-client.ca')
191
with open(ca_path, 'w') as fh:
192
fh.write(b64decode(rdata['ssl_ca']))
193
ctxt['database_ssl_ca'] = ca_path
194
elif 'ssl_ca' in rdata:
195
log("Charm not setup for ssl support but ssl ca found")
197
if 'ssl_cert' in rdata:
198
cert_path = os.path.join(
199
ssl_dir, 'db-client.cert')
200
if not os.path.exists(cert_path):
201
log("Waiting 1m for ssl client cert validity")
203
with open(cert_path, 'w') as fh:
204
fh.write(b64decode(rdata['ssl_cert']))
205
ctxt['database_ssl_cert'] = cert_path
206
key_path = os.path.join(ssl_dir, 'db-client.key')
207
with open(key_path, 'w') as fh:
208
fh.write(b64decode(rdata['ssl_key']))
209
ctxt['database_ssl_key'] = key_path
120
213
class IdentityServiceContext(OSContextGenerator):
127
220
for rid in relation_ids('identity-service'):
128
221
for unit in related_units(rid):
222
rdata = relation_get(rid=rid, unit=unit)
130
'service_port': relation_get('service_port', rid=rid,
132
'service_host': relation_get('service_host', rid=rid,
134
'auth_host': relation_get('auth_host', rid=rid, unit=unit),
135
'auth_port': relation_get('auth_port', rid=rid, unit=unit),
136
'admin_tenant_name': relation_get('service_tenant',
138
'admin_user': relation_get('service_username', rid=rid,
140
'admin_password': relation_get('service_password', rid=rid,
142
# XXX: Hard-coded http.
143
'service_protocol': 'http',
144
'auth_protocol': 'http',
224
'service_port': rdata.get('service_port'),
225
'service_host': rdata.get('service_host'),
226
'auth_host': rdata.get('auth_host'),
227
'auth_port': rdata.get('auth_port'),
228
'admin_tenant_name': rdata.get('service_tenant'),
229
'admin_user': rdata.get('service_username'),
230
'admin_password': rdata.get('service_password'),
232
rdata.get('service_protocol') or 'http',
234
rdata.get('auth_protocol') or 'http',
146
236
if context_complete(ctxt):
237
# NOTE(jamespage) this is required for >= icehouse
238
# so a missing value just indicates keystone needs
240
ctxt['admin_tenant_id'] = rdata.get('service_tenant_id')
179
276
'rabbitmq_virtual_host': vhost,
279
ssl_port = relation_get('ssl_port', rid=rid, unit=unit)
281
ctxt['rabbit_ssl_port'] = ssl_port
282
ssl_ca = relation_get('ssl_ca', rid=rid, unit=unit)
284
ctxt['rabbit_ssl_ca'] = ssl_ca
286
if relation_get('ha_queues', rid=rid, unit=unit) is not None:
287
ctxt['rabbitmq_ha_queues'] = True
289
ha_vip_only = relation_get('ha-vip-only',
290
rid=rid, unit=unit) is not None
181
292
if context_complete(ctxt):
293
if 'rabbit_ssl_ca' in ctxt:
295
log(("Charm not setup for ssl support "
298
ca_path = os.path.join(
299
self.ssl_dir, 'rabbit-client-ca.pem')
300
with open(ca_path, 'w') as fh:
301
fh.write(b64decode(ctxt['rabbit_ssl_ca']))
302
ctxt['rabbit_ssl_ca'] = ca_path
182
303
# Sufficient information found = break out!
184
305
# Used for active/active rabbitmq >= grizzly
185
ctxt['rabbitmq_hosts'] = []
186
for unit in related_units(rid):
187
ctxt['rabbitmq_hosts'].append(relation_get('private-address',
306
if ('clustered' not in ctxt or ha_vip_only) \
307
and len(related_units(rid)) > 1:
309
for unit in related_units(rid):
310
rabbitmq_hosts.append(relation_get('private-address',
312
ctxt['rabbitmq_hosts'] = ','.join(rabbitmq_hosts)
189
313
if not context_complete(ctxt):
341
470
'private_address': unit_get('private-address'),
344
for ext_port in self.external_ports:
345
if peer_units() or is_clustered():
346
int_port = determine_haproxy_port(ext_port)
348
int_port = determine_api_port(ext_port)
474
ctxt['private_address'] = config('vip')
475
for api_port in self.external_ports:
476
ext_port = determine_apache_port(api_port)
477
int_port = determine_api_port(api_port)
349
478
portmap = (int(ext_port), int(int_port))
350
479
ctxt['endpoints'].append(portmap)
354
class NeutronContext(object):
483
class NeutronContext(OSContextGenerator):
385
514
def ovs_ctxt(self):
386
515
driver = neutron_plugin_attribute(self.plugin, 'driver',
387
516
self.network_manager)
517
config = neutron_plugin_attribute(self.plugin, 'config',
518
self.network_manager)
390
520
'core_plugin': driver,
391
521
'neutron_plugin': 'ovs',
392
522
'neutron_security_groups': self.neutron_security_groups,
393
523
'local_ip': unit_private_ip(),
530
driver = neutron_plugin_attribute(self.plugin, 'driver',
531
self.network_manager)
532
config = neutron_plugin_attribute(self.plugin, 'config',
533
self.network_manager)
535
'core_plugin': driver,
536
'neutron_plugin': 'nvp',
537
'neutron_security_groups': self.neutron_security_groups,
538
'local_ip': unit_private_ip(),
544
def neutron_ctxt(self):
552
host = unit_get('private-address')
553
url = '%s://%s:%s' % (proto, host, '9696')
555
'network_manager': self.network_manager,
398
560
def __call__(self):
399
561
self._ensure_packages()
404
566
if not self.plugin:
407
ctxt = {'network_manager': self.network_manager}
569
ctxt = self.neutron_ctxt()
409
571
if self.plugin == 'ovs':
410
572
ctxt.update(self.ovs_ctxt())
573
elif self.plugin == 'nvp':
574
ctxt.update(self.nvp_ctxt())
576
alchemy_flags = config('neutron-alchemy-flags')
578
flags = config_flags_parser(alchemy_flags)
579
ctxt['neutron_alchemy_flags'] = flags
412
581
self._save_flag_file()
416
585
class OSConfigFlagContext(OSContextGenerator):
418
Responsible adding user-defined config-flags in charm config to a
419
to a template context.
588
Responsible for adding user-defined config-flags in charm config to a
591
NOTE: the value of config-flags may be a comma-separated list of
592
key=value pairs and some Openstack config files support
593
comma-separated lists as values.
421
596
def __call__(self):
422
597
config_flags = config('config-flags')
423
if not config_flags or config_flags in ['None', '']:
425
config_flags = config_flags.split(',')
427
for flag in config_flags:
429
log('Improperly formatted config-flag, expected k=v '
430
'got %s' % flag, level=WARNING)
432
k, v = flag.split('=')
434
ctxt = {'user_config_flags': flags}
601
flags = config_flags_parser(config_flags)
602
return {'user_config_flags': flags}
438
605
class SubordinateConfigContext(OSContextGenerator):
440
608
Responsible for inspecting relations to subordinates that
441
609
may be exporting required config via a json blob.