~celebdor/charms/trusty/nova-cloud-controller/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/openstack/context.py

  • Committer: James Page
  • Date: 2015-10-22 13:22:39 UTC
  • Revision ID: james.page@ubuntu.com-20151022132239-y17tbt1iwnjw30fk
Tags: 15.10
15.10 Charm release

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# You should have received a copy of the GNU Lesser General Public License
15
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
16
16
 
 
17
import glob
17
18
import json
18
19
import os
19
20
import re
50
51
from charmhelpers.core.strutils import bool_from_string
51
52
 
52
53
from charmhelpers.core.host import (
 
54
    get_bond_master,
 
55
    is_phy_iface,
53
56
    list_nics,
54
57
    get_nic_hwaddr,
55
58
    mkdir,
192
195
class OSContextGenerator(object):
193
196
    """Base class for all context generators."""
194
197
    interfaces = []
 
198
    related = False
 
199
    complete = False
 
200
    missing_data = []
195
201
 
196
202
    def __call__(self):
197
203
        raise NotImplementedError
198
204
 
 
205
    def context_complete(self, ctxt):
 
206
        """Check for missing data for the required context data.
 
207
        Set self.missing_data if it exists and return False.
 
208
        Set self.complete if no missing data and return True.
 
209
        """
 
210
        # Fresh start
 
211
        self.complete = False
 
212
        self.missing_data = []
 
213
        for k, v in six.iteritems(ctxt):
 
214
            if v is None or v == '':
 
215
                if k not in self.missing_data:
 
216
                    self.missing_data.append(k)
 
217
 
 
218
        if self.missing_data:
 
219
            self.complete = False
 
220
            log('Missing required data: %s' % ' '.join(self.missing_data), level=INFO)
 
221
        else:
 
222
            self.complete = True
 
223
        return self.complete
 
224
 
 
225
    def get_related(self):
 
226
        """Check if any of the context interfaces have relation ids.
 
227
        Set self.related and return True if one of the interfaces
 
228
        has relation ids.
 
229
        """
 
230
        # Fresh start
 
231
        self.related = False
 
232
        try:
 
233
            for interface in self.interfaces:
 
234
                if relation_ids(interface):
 
235
                    self.related = True
 
236
            return self.related
 
237
        except AttributeError as e:
 
238
            log("{} {}"
 
239
                "".format(self, e), 'INFO')
 
240
            return self.related
 
241
 
199
242
 
200
243
class SharedDBContext(OSContextGenerator):
201
244
    interfaces = ['shared-db']
211
254
        self.database = database
212
255
        self.user = user
213
256
        self.ssl_dir = ssl_dir
 
257
        self.rel_name = self.interfaces[0]
214
258
 
215
259
    def __call__(self):
216
260
        self.database = self.database or config('database')
244
288
            password_setting = self.relation_prefix + '_password'
245
289
 
246
290
        for rid in relation_ids(self.interfaces[0]):
 
291
            self.related = True
247
292
            for unit in related_units(rid):
248
293
                rdata = relation_get(rid=rid, unit=unit)
249
294
                host = rdata.get('db_host')
255
300
                    'database_password': rdata.get(password_setting),
256
301
                    'database_type': 'mysql'
257
302
                }
258
 
                if context_complete(ctxt):
 
303
                if self.context_complete(ctxt):
259
304
                    db_ssl(rdata, ctxt, self.ssl_dir)
260
305
                    return ctxt
261
306
        return {}
276
321
 
277
322
        ctxt = {}
278
323
        for rid in relation_ids(self.interfaces[0]):
 
324
            self.related = True
279
325
            for unit in related_units(rid):
280
326
                rel_host = relation_get('host', rid=rid, unit=unit)
281
327
                rel_user = relation_get('user', rid=rid, unit=unit)
285
331
                        'database_user': rel_user,
286
332
                        'database_password': rel_passwd,
287
333
                        'database_type': 'postgresql'}
288
 
                if context_complete(ctxt):
 
334
                if self.context_complete(ctxt):
289
335
                    return ctxt
290
336
 
291
337
        return {}
346
392
            ctxt['signing_dir'] = cachedir
347
393
 
348
394
        for rid in relation_ids(self.rel_name):
 
395
            self.related = True
349
396
            for unit in related_units(rid):
350
397
                rdata = relation_get(rid=rid, unit=unit)
351
398
                serv_host = rdata.get('service_host')
364
411
                             'service_protocol': svc_protocol,
365
412
                             'auth_protocol': auth_protocol})
366
413
 
367
 
                if context_complete(ctxt):
 
414
                if self.context_complete(ctxt):
368
415
                    # NOTE(jamespage) this is required for >= icehouse
369
416
                    # so a missing value just indicates keystone needs
370
417
                    # upgrading
403
450
        ctxt = {}
404
451
        for rid in relation_ids(self.rel_name):
405
452
            ha_vip_only = False
 
453
            self.related = True
406
454
            for unit in related_units(rid):
407
455
                if relation_get('clustered', rid=rid, unit=unit):
408
456
                    ctxt['clustered'] = True
435
483
                ha_vip_only = relation_get('ha-vip-only',
436
484
                                           rid=rid, unit=unit) is not None
437
485
 
438
 
                if context_complete(ctxt):
 
486
                if self.context_complete(ctxt):
439
487
                    if 'rabbit_ssl_ca' in ctxt:
440
488
                        if not self.ssl_dir:
441
489
                            log("Charm not setup for ssl support but ssl ca "
467
515
            ctxt['oslo_messaging_flags'] = config_flags_parser(
468
516
                oslo_messaging_flags)
469
517
 
470
 
        if not context_complete(ctxt):
 
518
        if not self.complete:
471
519
            return {}
472
520
 
473
521
        return ctxt
505
553
        if not os.path.isdir('/etc/ceph'):
506
554
            os.mkdir('/etc/ceph')
507
555
 
508
 
        if not context_complete(ctxt):
 
556
        if not self.context_complete(ctxt):
509
557
            return {}
510
558
 
511
559
        ensure_packages(['ceph-common'])
892
940
                'neutron_url': '%s://%s:%s' % (proto, host, '9696')}
893
941
        return ctxt
894
942
 
 
943
    def pg_ctxt(self):
 
944
        driver = neutron_plugin_attribute(self.plugin, 'driver',
 
945
                                          self.network_manager)
 
946
        config = neutron_plugin_attribute(self.plugin, 'config',
 
947
                                          self.network_manager)
 
948
        ovs_ctxt = {'core_plugin': driver,
 
949
                    'neutron_plugin': 'plumgrid',
 
950
                    'neutron_security_groups': self.neutron_security_groups,
 
951
                    'local_ip': unit_private_ip(),
 
952
                    'config': config}
 
953
        return ovs_ctxt
 
954
 
895
955
    def __call__(self):
896
956
        if self.network_manager not in ['quantum', 'neutron']:
897
957
            return {}
911
971
            ctxt.update(self.calico_ctxt())
912
972
        elif self.plugin == 'vsp':
913
973
            ctxt.update(self.nuage_ctxt())
 
974
        elif self.plugin == 'plumgrid':
 
975
            ctxt.update(self.pg_ctxt())
914
976
 
915
977
        alchemy_flags = config('neutron-alchemy-flags')
916
978
        if alchemy_flags:
922
984
 
923
985
 
924
986
class NeutronPortContext(OSContextGenerator):
925
 
    NIC_PREFIXES = ['eth', 'bond']
926
987
 
927
988
    def resolve_ports(self, ports):
928
989
        """Resolve NICs not yet bound to bridge(s)
934
995
 
935
996
        hwaddr_to_nic = {}
936
997
        hwaddr_to_ip = {}
937
 
        for nic in list_nics(self.NIC_PREFIXES):
 
998
        for nic in list_nics():
 
999
            # Ignore virtual interfaces (bond masters will be identified from
 
1000
            # their slaves)
 
1001
            if not is_phy_iface(nic):
 
1002
                continue
 
1003
 
 
1004
            _nic = get_bond_master(nic)
 
1005
            if _nic:
 
1006
                log("Replacing iface '%s' with bond master '%s'" % (nic, _nic),
 
1007
                    level=DEBUG)
 
1008
                nic = _nic
 
1009
 
938
1010
            hwaddr = get_nic_hwaddr(nic)
939
1011
            hwaddr_to_nic[hwaddr] = nic
940
1012
            addresses = get_ipv4_addr(nic, fatal=False)
960
1032
                # trust it to be the real external network).
961
1033
                resolved.append(entry)
962
1034
 
963
 
        return resolved
 
1035
        # Ensure no duplicates
 
1036
        return list(set(resolved))
964
1037
 
965
1038
 
966
1039
class OSConfigFlagContext(OSContextGenerator):
1279
1352
    def __call__(self):
1280
1353
        ports = config('data-port')
1281
1354
        if ports:
 
1355
            # Map of {port/mac:bridge}
1282
1356
            portmap = parse_data_port_mappings(ports)
1283
 
            ports = portmap.values()
 
1357
            ports = portmap.keys()
 
1358
            # Resolve provided ports or mac addresses and filter out those
 
1359
            # already attached to a bridge.
1284
1360
            resolved = self.resolve_ports(ports)
 
1361
            # FIXME: is this necessary?
1285
1362
            normalized = {get_nic_hwaddr(port): port for port in resolved
1286
1363
                          if port not in ports}
1287
1364
            normalized.update({port: port for port in resolved
1288
1365
                               if port in ports})
1289
1366
            if resolved:
1290
 
                return {bridge: normalized[port] for bridge, port in
 
1367
                return {normalized[port]: bridge for port, bridge in
1291
1368
                        six.iteritems(portmap) if port in normalized.keys()}
1292
1369
 
1293
1370
        return None
1298
1375
    def __call__(self):
1299
1376
        ctxt = {}
1300
1377
        mappings = super(PhyNICMTUContext, self).__call__()
1301
 
        if mappings and mappings.values():
1302
 
            ports = mappings.values()
 
1378
        if mappings and mappings.keys():
 
1379
            ports = sorted(mappings.keys())
1303
1380
            napi_settings = NeutronAPIContext()()
1304
1381
            mtu = napi_settings.get('network_device_mtu')
 
1382
            all_ports = set()
 
1383
            # If any of ports is a vlan device, its underlying device must have
 
1384
            # mtu applied first.
 
1385
            for port in ports:
 
1386
                for lport in glob.glob("/sys/class/net/%s/lower_*" % port):
 
1387
                    lport = os.path.basename(lport)
 
1388
                    all_ports.add(lport.split('_')[1])
 
1389
 
 
1390
            all_ports = list(all_ports)
 
1391
            all_ports.extend(ports)
1305
1392
            if mtu:
1306
 
                ctxt["devs"] = '\\n'.join(ports)
 
1393
                ctxt["devs"] = '\\n'.join(all_ports)
1307
1394
                ctxt['mtu'] = mtu
1308
1395
 
1309
1396
        return ctxt
1335
1422
                    'auth_protocol':
1336
1423
                    rdata.get('auth_protocol') or 'http',
1337
1424
                }
1338
 
                if context_complete(ctxt):
 
1425
                if self.context_complete(ctxt):
1339
1426
                    return ctxt
1340
1427
        return {}