File: api.py

package info (click to toggle)
python-os-ken 4.1.1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,396 kB
  • sloc: python: 100,059; erlang: 14,517; ansic: 594; sh: 338; makefile: 136
file content (438 lines) | stat: -rw-r--r-- 13,006 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# Copyright (c) 2014 Rackspace Hosting
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import uuid

from os_ken.lib import dpid as dpidlib
from os_ken.services.protocols.ovsdb import event as ovsdb_event


def _get_table_row(table, attr_name, attr_value, tables):
    sentinel = object()

    for row in tables[table].rows.values():
        if getattr(row, attr_name, sentinel) == attr_value:
            return row


def _get_controller(tables, attr_val, attr_name='target'):
    return _get_table_row('Controller', attr_name, attr_val, tables=tables)


def _get_bridge(tables, attr_val, attr_name='name'):
    return _get_table_row('Bridge', attr_name, attr_val, tables=tables)


def _get_port(tables, attr_val, attr_name='name'):
    return _get_table_row('Port', attr_name, attr_val, tables=tables)


def _get_iface(tables, attr_val, attr_name='name'):
    return _get_table_row('Interface', attr_name, attr_val, tables=tables)


def match_row(manager, system_id, table, fn):
    def _match_row(tables):
        return next((r for r in tables[table].rows.values()
                     if fn(r)), None)

    request_to_get_tables = ovsdb_event.EventReadRequest(system_id,
                                                         _match_row)
    reply_to_get_tables = manager.send_request(request_to_get_tables)
    return reply_to_get_tables.result


def match_rows(manager, system_id, table, fn):
    def _match_rows(tables):
        return (r for r in tables[table].rows.values() if fn(r))

    request = ovsdb_event.EventReadRequest(system_id, _match_rows)
    reply = manager.send_request(request)
    return reply.result


def row_by_name(manager, system_id, name, table='Bridge', fn=None):
    matched_row = match_row(manager, system_id, table,
                            lambda row: row.name == name)

    if fn is not None:
        return fn(matched_row)

    return matched_row


def rows_by_external_id(manager, system_id, key, value,
                        table='Bridge', fn=None):
    matched_rows = match_rows(manager, system_id, table,
                              lambda r: (key in r.external_ids and
                                         r.external_ids.get(key) == value))

    if matched_rows and fn is not None:
        return [fn(row) for row in matched_rows]

    return matched_rows


def rows_by_other_config(manager, system_id, key, value,
                         table='Bridge', fn=None):
    matched_rows = match_rows(manager, system_id, table,
                              lambda r: (key in r.other_config and
                                         r.other_config.get(key) == value))

    if matched_rows and fn is not None:
        return [fn(row) for row in matched_rows]

    return matched_rows


def get_column_value(manager, table, record, column):
    """
    Example : To get datapath_id from Bridge table
    get_column_value('Bridge', <bridge name>, 'datapath_id').strip('"')
    """
    row = row_by_name(manager, record, table)
    value = getattr(row, column, "")

    if isinstance(value, list) and len(value) == 1:
        value = value[0]

    return str(value)


def get_iface_by_name(manager, system_id, name, fn=None):
    iface = row_by_name(manager, system_id, name, 'Interface')

    if fn is not None:
        return fn(iface)

    return iface


def get_ifaces_by_external_id(manager, system_id, key, value, fn=None):
    return rows_by_external_id(manager, system_id, key, value,
                               'Interface', fn)


def get_ifaces_by_other_config(manager, system_id, key, value, fn=None):
    return rows_by_other_config(manager, system_id, key, value,
                                'Interface', fn)


def get_port_by_name(manager, system_id, name, fn=None):
    port = row_by_name(manager, system_id, name, 'Port')

    if fn is not None:
        return fn(port)

    return port


def get_bridge_for_iface_name(manager, system_id, iface_name, fn=None):
    iface = row_by_name(manager, system_id, iface_name, 'Interface')
    port = match_row(manager, system_id, 'Port',
                     lambda x: iface in x.interfaces)
    bridge = match_row(manager, system_id, 'Bridge',
                       lambda x: port in x.ports)

    if fn is not None:
        return fn(bridge)

    return bridge


def get_table(manager, system_id, name):
    def _get_table(tables):
        return tables[name]

    request_to_get_tables = ovsdb_event.EventReadRequest(system_id,
                                                         _get_table)
    reply_to_get_tables = manager.send_request(request_to_get_tables)
    return reply_to_get_tables.result


def get_bridge_by_datapath_id(manager, system_id, datapath_id, fn=None):
    def _match_fn(row):
        row_dpid = dpidlib.str_to_dpid(str(row.datapath_id[0]))
        return row_dpid == datapath_id

    bridge = match_row(manager, system_id, 'Bridge', _match_fn)

    if fn is not None:
        return fn(bridge)

    return bridge


def get_datapath_ids_for_systemd_id(manager, system_id):
    def _get_dp_ids(tables):
        dp_ids = []

        bridges = tables.get('Bridge')

        if not bridges:
            return dp_ids

        for bridge in bridges.rows.values():
            datapath_ids = bridge.datapath_id
            dp_ids.extend(dpidlib.str_to_dpid(dp_id) for dp_id in datapath_ids)

        return dp_ids

    request = ovsdb_event.EventReadRequest(system_id, _get_dp_ids)
    reply = manager.send_request(request)
    return reply.result


def get_system_id_for_datapath_id(manager, datapath_id):
    def _get_dp_ids(tables):
        bridges = tables.get('Bridge')

        if not bridges:
            return None

        for bridge in bridges.rows.values():
            datapath_ids = [dpidlib.str_to_dpid(dp_id)
                            for dp_id in bridge.datapath_id]

            if datapath_id in datapath_ids:
                openvswitch = tables['Open_vSwitch'].rows

                if openvswitch:
                    row = openvswitch.get(list(openvswitch.keys())[0])
                    return row.external_ids.get('system-id')

        return None

    request = ovsdb_event.EventReadRequest(None, _get_dp_ids)
    reply = manager.send_request(request)

    # NOTE(jkoelker) Bulk reads return a tuple of (system_id, result)
    for result in reply.result:
        if result[1]:
            return result[0]

    return None


def get_bridges_by_system_id(manager, system_id, fn=None):
    bridges = get_table(manager, system_id, 'Bridge').rows.values()

    if fn is not None:
        return fn(bridges)

    return bridges


def bridge_exists(manager, system_id, bridge_name):
    return bool(row_by_name(manager, system_id, bridge_name))


def port_exists(manager, system_id, port_name):
    return bool(row_by_name(manager, system_id, port_name, 'Port'))


def set_external_id(manager, system_id, key, val, fn):
    val = str(val)

    def _set_iface_external_id(tables, *_):
        row = fn(tables)

        if not row:
            return None

        external_ids = row.external_ids
        external_ids[key] = val
        row.external_ids = external_ids

    req = ovsdb_event.EventModifyRequest(system_id, _set_iface_external_id)
    return manager.send_request(req)


def set_iface_external_id(manager, system_id, iface_name, key, val):
    return set_external_id(manager, system_id, key, val,
                           lambda tables: _get_iface(tables, iface_name))


def set_other_config(manager, system_id, key, val, fn):
    val = str(val)

    def _set_iface_other_config(tables, *_):
        row = fn(tables)

        if not row:
            return None

        other_config = row.other_config
        other_config[key] = val
        row.other_config = other_config

    req = ovsdb_event.EventModifyRequest(system_id, _set_iface_other_config)
    return manager.send_request(req)


def set_iface_other_config(manager, system_id, iface_name, key, val):
    return set_other_config(manager, system_id, key, val,
                            lambda tables: _get_iface(tables, iface_name))


def del_external_id(manager, system_id, key, fn):
    def _del_iface_external_id(tables, *_):
        row = fn(tables)

        if not row:
            return None

        external_ids = row.external_ids
        if key in external_ids:
            external_ids.pop(key)
            row.external_ids = external_ids

    req = ovsdb_event.EventModifyRequest(system_id, _del_iface_external_id)
    return manager.send_request(req)


def del_iface_external_id(manager, system_id, iface_name, key):
    return del_external_id(manager, system_id, key,
                           lambda tables: _get_iface(tables, iface_name))


def del_other_config(manager, system_id, key, fn):
    def _del_iface_other_config(tables, *_):
        row = fn(tables)

        if not row:
            return None

        other_config = row.other_config
        if key in other_config:
            other_config.pop(key)
            row.other_config = other_config

    req = ovsdb_event.EventModifyRequest(system_id, _del_iface_other_config)
    return manager.send_request(req)


def del_iface_other_config(manager, system_id, iface_name, key):
    return del_other_config(manager, system_id, key,
                            lambda tables: _get_iface(tables, iface_name))


def del_port(manager, system_id, bridge_name, fn):
    def _delete_port(tables, *_):
        bridge = _get_bridge(tables, bridge_name)

        if not bridge:
            return

        port = fn(tables)

        if not port:
            return

        ports = bridge.ports
        ports.remove(port)
        bridge.ports = ports

    req = ovsdb_event.EventModifyRequest(system_id, _delete_port)

    return manager.send_request(req)


def del_port_by_uuid(manager, system_id, bridge_name, port_uuid):
    return del_port(manager, system_id, bridge_name,
                    lambda tables: _get_port(tables, port_uuid,
                                             attr_name='uuid'))


def del_port_by_name(manager, system_id, bridge_name, port_name):
    return del_port(manager, system_id, bridge_name,
                    lambda tables: _get_port(tables, port_name))


def set_controller(manager, system_id, bridge_name,
                   target, controller_info=None):
    controller_info = controller_info or {}

    def _set_controller(tables, insert):
        bridge = _get_bridge(tables, bridge_name)

        controller = _get_controller(tables, target)
        _uuid = None
        if not controller:
            _uuid = controller_info.get('uuid', uuid.uuid4())
            controller = insert(tables['Controller'], _uuid)
            controller.target = target
            controller.connection_mode = ['out-of-band']

        elif 'out-of-band' not in controller.connection_mode:
            controller.connection_mode = ['out-of-band']

        if controller_info:
            for key, val in controller_info.items():
                setattr(controller, key, val)

        bridge.controller = [controller]

        return _uuid

    req = ovsdb_event.EventModifyRequest(system_id, _set_controller)
    return manager.send_request(req)


def create_port(manager, system_id, bridge_name, port_info, iface_info=None,
                port_insert_uuid=None, iface_insert_uuid=None):
    if iface_info is None:
        iface_info = {}

    if not port_insert_uuid:
        port_insert_uuid = uuid.uuid4()

    if not iface_insert_uuid:
        iface_insert_uuid = uuid.uuid4()

    def _create_port(tables, insert):
        bridge = _get_bridge(tables, bridge_name)

        if not bridge:
            return

        default_port_name = 'port' + str(port_insert_uuid)

        if 'name' not in iface_info:
            iface_info['name'] = port_info.get('name', default_port_name)

        if 'type' not in iface_info:
            iface_info['type'] = 'internal'

        if 'name' not in port_info:
            port_info['name'] = default_port_name

        iface = insert(tables['Interface'], iface_insert_uuid)
        for key, val in iface_info.items():
            setattr(iface, key, val)

        port = insert(tables['Port'], port_insert_uuid)
        for key, val in port_info.items():
            setattr(port, key, val)

        port.interfaces = [iface]

        bridge.ports = bridge.ports + [port]

        return port_insert_uuid, iface_insert_uuid

    req = ovsdb_event.EventModifyRequest(system_id, _create_port)

    return manager.send_request(req)