~cloud-init-dev/cloud-init/trunk

« back to all changes in this revision

Viewing changes to cloudinit/net/network_state.py

support network rendering to sysconfig (for centos and RHEL)

This intends to add support for rendering of network data under sysconfig
distributions (centos and rhel).  The end result will be support for
network configuration via ConfigDrive or NoCloud on these OS.

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
    """
39
39
    state = None
40
40
    if 'version' in net_config and 'config' in net_config:
41
 
        ns = NetworkState(version=net_config.get('version'),
42
 
                          config=net_config.get('config'))
43
 
        ns.parse_config(skip_broken=skip_broken)
44
 
        state = ns.network_state
 
41
        nsi = NetworkStateInterpreter(version=net_config.get('version'),
 
42
                                      config=net_config.get('config'))
 
43
        nsi.parse_config(skip_broken=skip_broken)
 
44
        state = nsi.network_state
45
45
    return state
46
46
 
47
47
 
57
57
 
58
58
 
59
59
def from_state_file(state_file):
60
 
    network_state = None
61
60
    state = util.read_conf(state_file)
62
 
    network_state = NetworkState()
63
 
    network_state.load(state)
64
 
    return network_state
 
61
    nsi = NetworkStateInterpreter()
 
62
    nsi.load(state)
 
63
    return nsi
65
64
 
66
65
 
67
66
def diff_keys(expected, actual):
113
112
                                                      parents, dct)
114
113
 
115
114
 
 
115
class NetworkState(object):
 
116
 
 
117
    def __init__(self, network_state, version=NETWORK_STATE_VERSION):
 
118
        self._network_state = copy.deepcopy(network_state)
 
119
        self._version = version
 
120
 
 
121
    @property
 
122
    def version(self):
 
123
        return self._version
 
124
 
 
125
    def iter_routes(self, filter_func=None):
 
126
        for route in self._network_state.get('routes', []):
 
127
            if filter_func is not None:
 
128
                if filter_func(route):
 
129
                    yield route
 
130
            else:
 
131
                yield route
 
132
 
 
133
    @property
 
134
    def dns_nameservers(self):
 
135
        try:
 
136
            return self._network_state['dns']['nameservers']
 
137
        except KeyError:
 
138
            return []
 
139
 
 
140
    @property
 
141
    def dns_searchdomains(self):
 
142
        try:
 
143
            return self._network_state['dns']['search']
 
144
        except KeyError:
 
145
            return []
 
146
 
 
147
    def iter_interfaces(self, filter_func=None):
 
148
        ifaces = self._network_state.get('interfaces', {})
 
149
        for iface in six.itervalues(ifaces):
 
150
            if filter_func is None:
 
151
                yield iface
 
152
            else:
 
153
                if filter_func(iface):
 
154
                    yield iface
 
155
 
 
156
 
116
157
@six.add_metaclass(CommandHandlerMeta)
117
 
class NetworkState(object):
 
158
class NetworkStateInterpreter(object):
118
159
 
119
160
    initial_network_state = {
120
161
        'interfaces': {},
126
167
    }
127
168
 
128
169
    def __init__(self, version=NETWORK_STATE_VERSION, config=None):
129
 
        self.version = version
130
 
        self.config = config
131
 
        self.network_state = copy.deepcopy(self.initial_network_state)
 
170
        self._version = version
 
171
        self._config = config
 
172
        self._network_state = copy.deepcopy(self.initial_network_state)
 
173
        self._parsed = False
 
174
 
 
175
    @property
 
176
    def network_state(self):
 
177
        return NetworkState(self._network_state, version=self._version)
132
178
 
133
179
    def dump(self):
134
180
        state = {
135
 
            'version': self.version,
136
 
            'config': self.config,
137
 
            'network_state': self.network_state,
 
181
            'version': self._version,
 
182
            'config': self._config,
 
183
            'network_state': self._network_state,
138
184
        }
139
185
        return util.yaml_dumps(state)
140
186
 
141
187
    def load(self, state):
142
188
        if 'version' not in state:
143
189
            LOG.error('Invalid state, missing version field')
144
 
            raise Exception('Invalid state, missing version field')
 
190
            raise ValueError('Invalid state, missing version field')
145
191
 
146
192
        required_keys = NETWORK_STATE_REQUIRED_KEYS[state['version']]
147
193
        missing_keys = diff_keys(required_keys, state)
155
201
            setattr(self, key, state[key])
156
202
 
157
203
    def dump_network_state(self):
158
 
        return util.yaml_dumps(self.network_state)
 
204
        return util.yaml_dumps(self._network_state)
159
205
 
160
206
    def parse_config(self, skip_broken=True):
161
207
        # rebuild network state
162
 
        for command in self.config:
 
208
        for command in self._config:
163
209
            command_type = command['type']
164
210
            try:
165
211
                handler = self.command_handlers[command_type]
189
235
        }
190
236
        '''
191
237
 
192
 
        interfaces = self.network_state.get('interfaces')
 
238
        interfaces = self._network_state.get('interfaces', {})
193
239
        iface = interfaces.get(command['name'], {})
194
240
        for param, val in command.get('params', {}).items():
195
241
            iface.update({param: val})
215
261
            'gateway': None,
216
262
            'subnets': subnets,
217
263
        })
218
 
        self.network_state['interfaces'].update({command.get('name'): iface})
 
264
        self._network_state['interfaces'].update({command.get('name'): iface})
219
265
        self.dump_network_state()
220
266
 
221
267
    @ensure_command_keys(['name', 'vlan_id', 'vlan_link'])
228
274
                    hwaddress ether BC:76:4E:06:96:B3
229
275
                    vlan-raw-device eth0
230
276
        '''
231
 
        interfaces = self.network_state.get('interfaces')
 
277
        interfaces = self._network_state.get('interfaces', {})
232
278
        self.handle_physical(command)
233
279
        iface = interfaces.get(command.get('name'), {})
234
280
        iface['vlan-raw-device'] = command.get('vlan_link')
263
309
        '''
264
310
 
265
311
        self.handle_physical(command)
266
 
        interfaces = self.network_state.get('interfaces')
 
312
        interfaces = self._network_state.get('interfaces')
267
313
        iface = interfaces.get(command.get('name'), {})
268
314
        for param, val in command.get('params').items():
269
315
            iface.update({param: val})
270
316
        iface.update({'bond-slaves': 'none'})
271
 
        self.network_state['interfaces'].update({iface['name']: iface})
 
317
        self._network_state['interfaces'].update({iface['name']: iface})
272
318
 
273
319
        # handle bond slaves
274
320
        for ifname in command.get('bond_interfaces'):
280
326
                # inject placeholder
281
327
                self.handle_physical(cmd)
282
328
 
283
 
            interfaces = self.network_state.get('interfaces')
 
329
            interfaces = self._network_state.get('interfaces', {})
284
330
            bond_if = interfaces.get(ifname)
285
331
            bond_if['bond-master'] = command.get('name')
286
332
            # copy in bond config into slave
287
333
            for param, val in command.get('params').items():
288
334
                bond_if.update({param: val})
289
 
            self.network_state['interfaces'].update({ifname: bond_if})
 
335
            self._network_state['interfaces'].update({ifname: bond_if})
290
336
 
291
337
    @ensure_command_keys(['name', 'bridge_interfaces', 'params'])
292
338
    def handle_bridge(self, command):
319
365
 
320
366
        # find one of the bridge port ifaces to get mac_addr
321
367
        # handle bridge_slaves
322
 
        interfaces = self.network_state.get('interfaces')
 
368
        interfaces = self._network_state.get('interfaces', {})
323
369
        for ifname in command.get('bridge_interfaces'):
324
370
            if ifname in interfaces:
325
371
                continue
330
376
            # inject placeholder
331
377
            self.handle_physical(cmd)
332
378
 
333
 
        interfaces = self.network_state.get('interfaces')
 
379
        interfaces = self._network_state.get('interfaces', {})
334
380
        self.handle_physical(command)
335
381
        iface = interfaces.get(command.get('name'), {})
336
382
        iface['bridge_ports'] = command['bridge_interfaces']
341
387
 
342
388
    @ensure_command_keys(['address'])
343
389
    def handle_nameserver(self, command):
344
 
        dns = self.network_state.get('dns')
 
390
        dns = self._network_state.get('dns')
345
391
        if 'address' in command:
346
392
            addrs = command['address']
347
393
            if not type(addrs) == list:
357
403
 
358
404
    @ensure_command_keys(['destination'])
359
405
    def handle_route(self, command):
360
 
        routes = self.network_state.get('routes')
 
406
        routes = self._network_state.get('routes', [])
361
407
        network, cidr = command['destination'].split("/")
362
408
        netmask = cidr2mask(int(cidr))
363
409
        route = {