~james-page/charms/trusty/nova-compute/disable-neutron-security-option

« back to all changes in this revision

Viewing changes to hooks/nova_compute_context.py

  • Committer: James Page
  • Date: 2013-10-15 12:04:13 UTC
  • mfrom: (46.1.83 nova-compute)
  • Revision ID: james.page@canonical.com-20131015120413-grclbw2ot5gbgp5r
Update of all Havana / Saucy / python-redux work:

* Full python rewrite using new OpenStack charm-helpers.

* Test coverage

* Havana support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
from charmhelpers.contrib.openstack import context
 
3
 
 
4
from charmhelpers.core.host import service_running, service_start
 
5
from charmhelpers.fetch import apt_install, filter_installed_packages
 
6
 
 
7
from charmhelpers.core.hookenv import (
 
8
    config,
 
9
    log,
 
10
    relation_get,
 
11
    relation_ids,
 
12
    related_units,
 
13
    service_name,
 
14
    unit_get,
 
15
    ERROR,
 
16
)
 
17
 
 
18
from charmhelpers.contrib.openstack.utils import get_host_ip, os_release
 
19
from charmhelpers.contrib.network.ovs import add_bridge
 
20
 
 
21
 
 
22
# This is just a label and it must be consistent across
 
23
# nova-compute nodes to support live migration.
 
24
CEPH_SECRET_UUID = '514c9fca-8cbe-11e2-9c52-3bc8c7819472'
 
25
 
 
26
OVS_BRIDGE = 'br-int'
 
27
 
 
28
 
 
29
def _save_flag_file(path, data):
 
30
    '''
 
31
    Saves local state about plugin or manager to specified file.
 
32
    '''
 
33
    # Wonder if we can move away from this now?
 
34
    if data is None:
 
35
        return
 
36
    with open(path, 'wb') as out:
 
37
        out.write(data)
 
38
 
 
39
 
 
40
# compatability functions to help with quantum -> neutron transition
 
41
def _network_manager():
 
42
    from nova_compute_utils import network_manager as manager
 
43
    return manager()
 
44
 
 
45
 
 
46
def _neutron_security_groups():
 
47
        '''
 
48
        Inspects current cloud-compute relation and determine if nova-c-c has
 
49
        instructed us to use neutron security groups.
 
50
        '''
 
51
        for rid in relation_ids('cloud-compute'):
 
52
            for unit in related_units(rid):
 
53
                groups = [
 
54
                    relation_get('neutron_security_groups',
 
55
                                 rid=rid, unit=unit),
 
56
                    relation_get('quantum_security_groups',
 
57
                                 rid=rid, unit=unit)
 
58
                ]
 
59
                if ('yes' in groups or 'Yes' in groups):
 
60
                    return True
 
61
        return False
 
62
 
 
63
 
 
64
def _neutron_plugin():
 
65
        from nova_compute_utils import neutron_plugin
 
66
        return neutron_plugin()
 
67
 
 
68
 
 
69
def _neutron_url(rid, unit):
 
70
        # supports legacy relation settings.
 
71
        return (relation_get('neutron_url', rid=rid, unit=unit) or
 
72
                relation_get('quantum_url', rid=rid, unit=unit))
 
73
 
 
74
 
 
75
class NovaComputeLibvirtContext(context.OSContextGenerator):
 
76
    '''
 
77
    Determines various libvirt and nova options depending on live migration
 
78
    configuration.
 
79
    '''
 
80
    interfaces = []
 
81
 
 
82
    def __call__(self):
 
83
        # distro defaults
 
84
        ctxt = {
 
85
            # /etc/default/libvirt-bin
 
86
            'libvirtd_opts': '-d',
 
87
            # /etc/libvirt/libvirtd.conf (
 
88
            'listen_tls': 0,
 
89
        }
 
90
 
 
91
        # enable tcp listening if configured for live migration.
 
92
        if config('enable-live-migration'):
 
93
            ctxt['libvirtd_opts'] += ' -l'
 
94
 
 
95
        if config('migration-auth-type') in ['none', 'None', 'ssh']:
 
96
            ctxt['listen_tls'] = 0
 
97
 
 
98
        if config('migration-auth-type') == 'ssh':
 
99
            # nova.conf
 
100
            ctxt['live_migration_uri'] = 'qemu+ssh://%s/system'
 
101
 
 
102
        return ctxt
 
103
 
 
104
 
 
105
class NovaComputeVirtContext(context.OSContextGenerator):
 
106
    interfaces = []
 
107
 
 
108
    def __call__(self):
 
109
        return {}
 
110
 
 
111
 
 
112
class NovaComputeCephContext(context.CephContext):
 
113
    def __call__(self):
 
114
        ctxt = super(NovaComputeCephContext, self).__call__()
 
115
        if not ctxt:
 
116
            return {}
 
117
        svc = service_name()
 
118
        # secret.xml
 
119
        ctxt['ceph_secret_uuid'] = CEPH_SECRET_UUID
 
120
        # nova.conf
 
121
        ctxt['service_name'] = svc
 
122
        ctxt['rbd_user'] = svc
 
123
        ctxt['rbd_secret_uuid'] = CEPH_SECRET_UUID
 
124
        ctxt['rbd_pool'] = 'nova'
 
125
 
 
126
        return ctxt
 
127
 
 
128
 
 
129
class CloudComputeContext(context.OSContextGenerator):
 
130
    '''
 
131
    Generates main context for writing nova.conf and quantum.conf templates
 
132
    from a cloud-compute relation changed hook.  Mainly used for determinig
 
133
    correct network and volume service configuration on the compute node,
 
134
    as advertised by the cloud-controller.
 
135
 
 
136
    Note: individual quantum plugin contexts are handled elsewhere.
 
137
    '''
 
138
    interfaces = ['cloud-compute']
 
139
 
 
140
    def _ensure_packages(self, packages):
 
141
        '''Install but do not upgrade required packages'''
 
142
        required = filter_installed_packages(packages)
 
143
        if required:
 
144
            apt_install(required, fatal=True)
 
145
 
 
146
    @property
 
147
    def network_manager(self):
 
148
        return _network_manager()
 
149
 
 
150
    @property
 
151
    def volume_service(self):
 
152
        volume_service = None
 
153
        for rid in relation_ids('cloud-compute'):
 
154
            for unit in related_units(rid):
 
155
                volume_service = relation_get('volume_service',
 
156
                                              rid=rid, unit=unit)
 
157
        return volume_service
 
158
 
 
159
    def flat_dhcp_context(self):
 
160
        ec2_host = None
 
161
        for rid in relation_ids('cloud-compute'):
 
162
            for unit in related_units(rid):
 
163
                ec2_host = relation_get('ec2_host', rid=rid, unit=unit)
 
164
 
 
165
        if not ec2_host:
 
166
            return {}
 
167
 
 
168
        if config('multi-host').lower() == 'yes':
 
169
            self._ensure_packages(['nova-api', 'nova-network'])
 
170
 
 
171
        return {
 
172
            'flat_interface': config('flat-interface'),
 
173
            'ec2_dmz_host': ec2_host,
 
174
        }
 
175
 
 
176
    def neutron_context(self):
 
177
        # generate config context for neutron or quantum. these get converted
 
178
        # directly into flags in nova.conf
 
179
        # NOTE: Its up to release templates to set correct driver
 
180
 
 
181
        def _legacy_quantum(ctxt):
 
182
            # rename neutron flags to support legacy quantum.
 
183
            renamed = {}
 
184
            for k, v in ctxt.iteritems():
 
185
                k = k.replace('neutron', 'quantum')
 
186
                renamed[k] = v
 
187
            return renamed
 
188
 
 
189
        neutron_ctxt = {'neutron_url': None}
 
190
        for rid in relation_ids('cloud-compute'):
 
191
            for unit in related_units(rid):
 
192
                rel = {'rid': rid, 'unit': unit}
 
193
 
 
194
                url = _neutron_url(**rel)
 
195
                if not url:
 
196
                    # only bother with units that have a neutron url set.
 
197
                    continue
 
198
 
 
199
                neutron_ctxt = {
 
200
                    'neutron_auth_strategy': 'keystone',
 
201
                    'keystone_host': relation_get(
 
202
                        'auth_host', **rel),
 
203
                    'auth_port': relation_get(
 
204
                        'auth_port', **rel),
 
205
                    'neutron_admin_tenant_name': relation_get(
 
206
                        'service_tenant_name', **rel),
 
207
                    'neutron_admin_username': relation_get(
 
208
                        'service_username', **rel),
 
209
                    'neutron_admin_password': relation_get(
 
210
                        'service_password', **rel),
 
211
                    'neutron_plugin': _neutron_plugin(),
 
212
                    'neutron_url': url,
 
213
                }
 
214
 
 
215
        missing = [k for k, v in neutron_ctxt.iteritems() if v in ['', None]]
 
216
        if missing:
 
217
            log('Missing required relation settings for Quantum: ' +
 
218
                ' '.join(missing))
 
219
            return {}
 
220
 
 
221
        neutron_ctxt['neutron_security_groups'] = _neutron_security_groups()
 
222
 
 
223
        ks_url = 'http://%s:%s/v2.0' % (neutron_ctxt['keystone_host'],
 
224
                                        neutron_ctxt['auth_port'])
 
225
        neutron_ctxt['neutron_admin_auth_url'] = ks_url
 
226
 
 
227
        if self.network_manager == 'quantum':
 
228
            return _legacy_quantum(neutron_ctxt)
 
229
 
 
230
        return neutron_ctxt
 
231
 
 
232
    def volume_context(self):
 
233
        # provide basic validation that the volume manager is supported on the
 
234
        # given openstack release (nova-volume is only supported for E and F)
 
235
        # it is up to release templates to set the correct volume driver.
 
236
 
 
237
        if not self.volume_service:
 
238
            return {}
 
239
 
 
240
        os_rel = os_release('nova-common')
 
241
 
 
242
        # ensure volume service is supported on specific openstack release.
 
243
        if self.volume_service == 'cinder':
 
244
            if os_rel == 'essex':
 
245
                e = ('Attempting to configure cinder volume manager on '
 
246
                     'an unsupported OpenStack release (essex)')
 
247
                log(e, level=ERROR)
 
248
                raise context.OSContextError(e)
 
249
            return 'cinder'
 
250
        elif self.volume_service == 'nova-volume':
 
251
            if os_release('nova-common') not in ['essex', 'folsom']:
 
252
                e = ('Attempting to configure nova-volume manager on '
 
253
                     'an unsupported OpenStack release (%s).' % os_rel)
 
254
                log(e, level=ERROR)
 
255
                raise context.OSContextError(e)
 
256
            return 'nova-volume'
 
257
        else:
 
258
            e = ('Invalid volume service received via cloud-compute: %s' %
 
259
                 self.volume_service)
 
260
            log(e, level=ERROR)
 
261
            raise context.OSContextError(e)
 
262
 
 
263
    def network_manager_context(self):
 
264
        ctxt = {}
 
265
        if self.network_manager == 'flatdhcpmanager':
 
266
            ctxt = self.flat_dhcp_context()
 
267
        elif self.network_manager in ['neutron', 'quantum']:
 
268
            ctxt = self.neutron_context()
 
269
 
 
270
        _save_flag_file(path='/etc/nova/nm.conf', data=self.network_manager)
 
271
 
 
272
        log('Generated config context for %s network manager.' %
 
273
            self.network_manager)
 
274
        return ctxt
 
275
 
 
276
    def __call__(self):
 
277
        rids = relation_ids('cloud-compute')
 
278
        if not rids:
 
279
            return {}
 
280
 
 
281
        ctxt = {}
 
282
 
 
283
        net_manager = self.network_manager_context()
 
284
        if net_manager:
 
285
            ctxt['network_manager'] = self.network_manager
 
286
            ctxt['network_manager_config'] = net_manager
 
287
 
 
288
        vol_service = self.volume_context()
 
289
        if vol_service:
 
290
            ctxt['volume_service'] = vol_service
 
291
 
 
292
        return ctxt
 
293
 
 
294
 
 
295
class NeutronComputeContext(context.NeutronContext):
 
296
    interfaces = []
 
297
 
 
298
    @property
 
299
    def plugin(self):
 
300
        return _neutron_plugin()
 
301
 
 
302
    @property
 
303
    def network_manager(self):
 
304
        return _network_manager()
 
305
 
 
306
    @property
 
307
    def neutron_security_groups(self):
 
308
        return _neutron_security_groups()
 
309
 
 
310
    def _ensure_bridge(self):
 
311
        if not service_running('openvswitch-switch'):
 
312
            service_start('openvswitch-switch')
 
313
        add_bridge(OVS_BRIDGE)
 
314
 
 
315
    def ovs_ctxt(self):
 
316
        # In addition to generating config context, ensure the OVS service
 
317
        # is running and the OVS bridge exists. Also need to ensure
 
318
        # local_ip points to actual IP, not hostname.
 
319
        ovs_ctxt = super(NeutronComputeContext, self).ovs_ctxt()
 
320
        if not ovs_ctxt:
 
321
            return {}
 
322
 
 
323
        self._ensure_bridge()
 
324
 
 
325
        ovs_ctxt['local_ip'] = get_host_ip(unit_get('private-address'))
 
326
        return ovs_ctxt