summaryrefslogtreecommitdiff
path: root/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py
blob: 0d16d3f100d5cc1f7457942bc2d9ee9182ac24a9 (plain)
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
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
# Copyright 2019 Red Hat, Inc.
#
#    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 abc

from ovsdbapp import api

from neutron.common.ovn import constants as ovn_const


class API(api.API, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def create_lswitch_port(self, lport_name, lswitch_name, may_exist=True,
                            **columns):
        """Create a command to add an OVN logical switch port

        :param lport_name:    The name of the lport
        :type lport_name:     string
        :param lswitch_name:  The name of the lswitch the lport is created on
        :type lswitch_name:   string
        :param may_exist:     Do not fail if lport already exists
        :type may_exist:      bool
        :param columns:       Dictionary of port columns
                              Supported columns: macs, external_ids,
                                                 parent_name, tag, enabled
        :type columns:        dictionary
        :returns:             :class:`Command` with no result
        """

    @abc.abstractmethod
    def set_lswitch_port(self, lport_name, if_exists=True, **columns):
        """Create a command to set OVN logical switch port fields

        :param lport_name:    The name of the lport
        :type lport_name:     string
        :param columns:       Dictionary of port columns
                              Supported columns: macs, external_ids,
                                                 parent_name, tag, enabled
        :param if_exists:     Do not fail if lport does not exist
        :type if_exists:      bool
        :type columns:        dictionary
        :returns:             :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_lswitch_port(self, lport_name=None, lswitch_name=None,
                            ext_id=None, if_exists=True):
        """Create a command to delete an OVN logical switch port

        :param lport_name:    The name of the lport
        :type lport_name:     string
        :param lswitch_name:  The name of the lswitch
        :type lswitch_name:   string
        :param ext_id:        The external id of the lport
        :type ext_id:         pair of <ext_id_key ,ext_id_value>
        :param if_exists:     Do not fail if the lport does not exist
        :type if_exists:      bool
        :returns:             :class:`Command` with no result
        """

    @abc.abstractmethod
    def create_lrouter(self, name, may_exist=True, **columns):
        """Create a command to add an OVN lrouter

        :param name:         The id of the lrouter
        :type name:          string
        :param may_exist:    Do not fail if lrouter already exists
        :type may_exist:     bool
        :param columns:      Dictionary of lrouter columns
                             Supported columns: external_ids, default_gw, ip
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def update_lrouter(self, name, if_exists=True, **columns):
        """Update a command to add an OVN lrouter

        :param name:         The id of the lrouter
        :type name:          string
        :param if_exists:    Do not fail if the lrouter does not exist
        :type if_exists:     bool
        :param columns:      Dictionary of lrouter columns
                             Supported columns: external_ids, default_gw, ip
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_lrouter(self, name, if_exists=True):
        """Create a command to delete an OVN lrouter

        :param name:         The id of the lrouter
        :type name:          string
        :param if_exists:    Do not fail if the lrouter does not exist
        :type if_exists:     bool
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def add_lrouter_port(self, name, lrouter, may_exist=True,
                         **columns):
        """Create a command to add an OVN lrouter port

        :param name:         The unique name of the lrouter port
        :type name:          string
        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param lswitch:      The unique name of the lswitch
        :type lswitch:       string
        :param may_exist:    If true, do not fail if lrouter port set
                             already exists.
        :type may_exist:     bool
        :param columns:      Dictionary of lrouter columns
                             Supported columns: external_ids, mac, network
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def update_lrouter_port(self, name, if_exists=True, **columns):
        """Update a command to add an OVN lrouter port

        :param name:         The unique name of the lrouter port
        :type name:          string
        :param if_exists:    Do not fail if the lrouter port does not exist
        :type if_exists:     bool
        :param columns:      Dictionary of lrouter columns
                             Supported columns: networks
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_lrouter_port(self, name, lrouter, if_exists=True):
        """Create a command to delete an OVN lrouter port

        :param name:         The unique name of the lport
        :type name:          string
        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param if_exists:    Do not fail if the lrouter port does not exist
        :type if_exists:     bool
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def set_lrouter_port_in_lswitch_port(
            self, lswitch_port, lrouter_port, is_gw_port=False, if_exists=True,
            lsp_address=ovn_const.DEFAULT_ADDR_FOR_LSP_WITH_PEER):
        """Create a command to set lswitch_port as lrouter_port

        :param lswitch_port: The name of logical switch port
        :type lswitch_port:  string
        :param lrouter_port: The name of logical router port
        :type lrouter_port:  string
        :param is_gw_port:   True if logical router port is gw port
        :type is_gw_port:    bool
        :param if_exists:    Do not fail if the lswitch port does not exist
        :type if_exists:     bool
        :param lsp_address:  logical switch port's addresses to set
        :type lsp_address:   string or list of strings
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def add_acl(self, lswitch, lport, **columns):
        """Create an ACL for a logical port.

        :param lswitch:      The logical switch the port is attached to.
        :type lswitch:       string
        :param lport:        The logical port this ACL is associated with.
        :type lport:         string
        :param columns:      Dictionary of ACL columns
                             Supported columns: see ACL table in OVN_Northbound
        :type columns:       dictionary
        """

    @abc.abstractmethod
    def delete_acl(self, lswitch, lport, if_exists=True):
        """Delete all ACLs for a logical port.

        :param lswitch:      The logical switch the port is attached to.
        :type lswitch:       string
        :param lport:        The logical port this ACL is associated with.
        :type lport:         string
        :param if_exists:    Do not fail if the ACL for this lport does not
                             exist
        :type if_exists:     bool
        """

    @abc.abstractmethod
    def update_acls(self, lswitch_names, port_list, acl_new_values_dict,
                    need_compare=True, is_add_acl=True):
        """Update the list of acls on logical switches with new values.

        :param lswitch_names:         List of logical switch names
        :type lswitch_name:           []
        :param port_list:             Iterator of list of ports
        :type port_list:              []
        :param acl_new_values_dict:   Dictionary of acls indexed by port id
        :type acl_new_values_dict:    {}
        :param need_compare:          If acl_new_values_dict need compare
                                      with existing acls
        :type need_compare:           bool
        :is_add_acl:                  If updating is caused by adding acl
        :type is_add_acl:             bool
        """

    @abc.abstractmethod
    def get_acl_by_id(self, acl_id):
        """Get an ACL by its ID.

        :param acl_id:                ID of the ACL to lookup
        :type acl_id:                 string
        :returns                      The ACL row or None:
        """

    @abc.abstractmethod
    def add_static_route(self, lrouter, **columns):
        """Add static route to logical router.

        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param columns:      Dictionary of static columns
                             Supported columns: prefix, nexthop, valid
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_static_route(self, lrouter, ip_prefix, nexthop, if_exists=True):
        """Delete static route from logical router.

        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param ip_prefix:    The prefix of the static route
        :type ip_prefix:     string
        :param nexthop:      The nexthop of the static route
        :type nexthop:       string
        :param if_exists:    Do not fail if router does not exist
        :type if_exists:     bool
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_address_set(self, name, if_exists=True):
        """Delete an address set

        :param name:        The name of the address set
        :type name:         string
        :param if_exists:   Do not fail if the address set does not exist
        :type if_exists:    bool
        :returns:           :class:`Command` with no result
        """

    @abc.abstractmethod
    def get_all_chassis_gateway_bindings(self,
                                         chassis_candidate_list=None):
        """Return a dictionary of chassis name:list of gateways

        :param chassis_candidate_list:  List of possible chassis candidates
        :type chassis_candidate_list:   []
        :returns:                       {} of chassis to routers mapping
        """

    @abc.abstractmethod
    def get_gateway_chassis_binding(self, gateway_id):
        """Return the list of chassis to which the gateway is bound to

        As one gateway can be hosted by multiple chassis, this method is
        returning a list of those chassis ordered by priority. This means
        that the first element of the list is the chassis hosting the
        gateway with the highest priority (which will likely be where
        the router port is going to be active).

        :param gateway_id:     The gateway id
        :type gateway_id:      string
        :returns:              a list of strings with the chassis names
        """

    @abc.abstractmethod
    def get_unhosted_gateways(self, port_physnet_dict, chassis_physnets,
                              gw_chassis):
        """Return a list of gateways not hosted on chassis

        :param port_physnet_dict: Dictionary of gateway ports and their physnet
        :param chassis_physnets:  Dictionary of chassis and physnets
        :param gw_chassis:        List of gateway chassis provided by admin
                                  through ovn-cms-options
        :returns:                 List of gateways not hosted on a valid
                                  chassis
        """

    @abc.abstractmethod
    def add_dhcp_options(self, subnet_id, port_id=None, may_exist=True,
                         **columns):
        """Adds the DHCP options specified in the @columns in DHCP_Options

        If the DHCP options already exist in the DHCP_Options table for
        the @subnet_id (and @lsp_name), updates the row, else creates a new
        row.

        :param subnet_id:      The subnet id to which the DHCP options belong
                               to
        :type subnet_id:       string
        :param port_id:        The port id to which the DHCP options belong to
                               if specified
        :type port_id:         string
        :param may_exist:      If true, checks if the DHCP options for
                               subnet_id exists or not. If it already exists,
                               it updates the row with the columns specified.
                               Else creates a new row.
        :type may_exist:       bool
        :type columns:         Dictionary of DHCP_Options columns
                               Supported columns: see DHCP_Options table in
                               OVN_Northbound
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_dhcp_options(self, row_uuid, if_exists=True):
        """Deletes the row in DHCP_Options with the @row_uuid

        :param row_uuid:       The UUID of the row to be deleted.
        :type row_uuid:        string
        :param if_exists:      Do not fail if the DHCP_Options row does not
                               exist
        :type if_exists:       bool
        """

    @abc.abstractmethod
    def get_subnet_dhcp_options(self, subnet_id, with_ports=False):
        """Returns the Subnet DHCP options as a dictionary

        :param subnet_id:      The subnet id whose DHCP options are returned
        :type subnet_id:       string
        :param with_ports:     If True, also returns the ports DHCP options.
        :type with_ports:      bool
        :returns:              Returns a dictionary containing two keys:
                               subnet and ports.
        """

    @abc.abstractmethod
    def get_subnets_dhcp_options(self, subnet_ids):
        """Returns the Subnets DHCP options as list of dictionary

        :param subnet_ids:     The subnet ids whose DHCP options are returned
        :type subnet_ids:      list of string
        :returns:              Returns the columns of the DHCP_Options as list
                               of dictionary. Empty list is returned if no
                               DHCP_Options matched found.
        """

    @abc.abstractmethod
    def get_address_sets(self):
        """Gets all address sets in the OVN_Northbound DB

        :returns: dictionary indexed by name, DB columns as values
        """

    @abc.abstractmethod
    def get_sg_port_groups(self):
        """Gets port groups in the OVN_Northbound DB that map to SGs.

        :returns: dictionary indexed by name, DB columns as values
        """

    @abc.abstractmethod
    def get_router_port_options(self, lsp_name):
        """Get options set for lsp of type router

        :returns: router port options
        """

    @abc.abstractmethod
    def add_nat_rule_in_lrouter(self, lrouter, **columns):
        """Add NAT rule in logical router


        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param columns:      Dictionary of nat columns
                             Supported columns: type, logical_ip, external_ip
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def delete_nat_rule_in_lrouter(self, lrouter, type, logical_ip,
                                   external_ip, if_exists=True):
        """Delete NAT rule in logical router

        :param lrouter:      The unique name of the lrouter
        :type lrouter:       string
        :param type:         Type of nat. Supported values are 'snat', 'dnat'
                             and 'dnat_and_snat'
        :type type:          string
        :param logical_ip:   IP or network that needs to be natted
        :type logical_ip:    string
        :param external_ip:  External IP to be used for nat
        :type external_ip:   string
        :param if_exists:    Do not fail if the Logical_Router row does not
                             exist
        :type if_exists:     bool
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def get_lrouter_nat_rules(self, lrouter):
        """Returns the nat rules of a router

        :param lrouter: The unique name of the router
        :type lrouter:  string
        :returns:       A list of nat rules of the router, with each item
                        as a dict with the keys - 'external_ip', 'logical_ip'
                        'type' and 'uuid' of the row.
        """

    @abc.abstractmethod
    def set_nat_rule_in_lrouter(self, lrouter, nat_rule_uuid, **columns):
        """Sets the NAT rule fields

        :param lrouter: The unique name of the router to which this the
                        NAT rule belongs to.
        :type lrouter:  string
        :param nat_rule_uuid:  The uuid of the NAT rule row to be updated.
        :type nat_rule_uuid:   string
        :type columns:       dictionary
        :returns:            :class:`Command` with no result
        """

    @abc.abstractmethod
    def get_lswitch(self, lswitch_name):
        """Returns the logical switch

        :param lswitch_name: The unique name of the logical switch
        :type lswitch_name: string
        :returns: Returns logical switch or None
        """

    @abc.abstractmethod
    def get_ls_and_dns_record(self, lswitch_name):
        """Returns the logical switch and 'dns' records

        :param lswitch_name: The unique name of the logical switch
        :type lswitch_name: string
        :returns: Returns logical switch and dns records as a tuple
        """

    @abc.abstractmethod
    def get_floatingip(self, fip_id):
        """Get a Floating IP by its ID

        :param fip_id: The floating IP id
        :type fip_id: string
        :returns: The NAT rule row or None
        """

    @abc.abstractmethod
    def get_floatingip_by_ips(self, router_id, logical_ip, external_ip):
        """Get a Floating IP based on it's logical and external IPs.

        DEPRECATED. In the Rocky release of OpenStack this method can be
        removed and get_floatingip() should be used instead. This method
        is a backward compatibility layer for the Pike -> Queens release.

        :param router_id: The ID of the router to which the FIP belongs to.
        :type lrouter:  string
        :param logical_ip: The FIP's logical IP address
        :type logical_ip: string
        :param external_ip: The FIP's external IP address
        :type external_ip: string
        :returns: The NAT rule row or None
        """

    def check_revision_number(self, name, resource, resource_type,
                              if_exists=True):
        """Compare the revision number from Neutron and OVN.

        Check if the revision number in OVN is lower than the one from
        the Neutron resource, otherwise raise RevisionConflict and abort
        the transaction.

        :param name:          The unique name of the resource
        :type name:           string
        :param resource:      The neutron resource object
        :type resource:       dictionary
        :param resource_type: The resource object type
        :type resource_type:  dictionary
        :param if_exists:     Do not fail if resource does not exist
        :type if_exists:      bool
        :returns:             :class:`Command` with no result
        :raise:               RevisionConflict if the revision number in
                              OVN is equal or higher than the neutron object
        """

    @abc.abstractmethod
    def get_lswitch_port(self, lsp_name):
        """Get a Logical Switch Port by its name.

        :param lsp_name: The Logical Switch Port name
        :type lsp_name: string
        :returns: The Logical Switch Port row or None
        """

    @abc.abstractmethod
    def get_lrouter(self, lrouter_name):
        """Get a Logical Router by its name

        :param lrouter_name: The name of the logical router
        :type lrouter_name: string
        :returns: The Logical_Router row or None
        """

    @abc.abstractmethod
    def delete_lrouter_ext_gw(self, lrouter_name):
        """Delete Logical Router external gateway.

        :param lrouter_name: The name of the logical router
        :type lrouter_name: string
        :returns: :class:`Command` with no result
        """

    @abc.abstractmethod
    def set_lswitch_port_to_virtual_type(self, lport_name, vip,
                                         virtual_parent, if_exists=True):
        """Set the type of a given port to "virtual".

        Set the type of a given port to "virtual" and all its related
        options.

        :param lport_name:      The name of the lport
        :type lport_name:       string
        :param vip:             The virtual ip
        :type vip:              string
        :param virtual_parent:  The name of the parent lport
        :type virtual_parent:   string
        :param if_exists:       Do not fail if lport does not exist
        :type if_exists:        bool
        :returns:               :class:`Command` with no result
        """

    @abc.abstractmethod
    def unset_lswitch_port_to_virtual_type(self, lport_name,
                                           virtual_parent, if_exists=True):
        """Unset the type of a given port from "virtual".

        Unset the type of a given port from "virtual" and all its related
        options.

        :param lport_name:      The name of the lport
        :type lport_name:       string
        :param virtual_parent:  The name of the parent lport
        :type virtual_parent:   string
        :param if_exists:       Do not fail if lport does not exist
        :type if_exists:        bool
        :returns:               :class:`Command` with no result
        """

    @abc.abstractmethod
    def update_lb_external_ids(self, lb_name, values, if_exists=True):
        """Set the external_ids field of a given Load Balancer.

        :param lb_name:         The name of the load_balancer
        :type lb_name:          string
        :param values:          Values to be set in external_ids
        :type values:           dict
        :param if_exists:       Do not fail if lb_name does not exist
        :type if_exists:        bool
        :returns:               :class:`Command` with no result
        """

    @abc.abstractmethod
    def get_router_floatingip_lbs(self, lrouter_name):
        """Get Load Balancers used as port forwarding by a Logical Router.

        :param lrouter_name: The name of the logical router
        :type lrouter_name: string
        :returns: a list of Load_Balancer rows matched
        """

    @abc.abstractmethod
    def get_floatingip_in_nat_or_lb(self, fip_id):
        """Get a Floating IP from either NAT or Load Balancer table by its ID

        NAT rows in OVN are mapped for floating IPs, except for port
        forwarding. In such cases, Load Balancer table is used . This function
        returns a row from NAT, if there is one. Otherwise, it will lookup
        for the FIP ID in the LB table and return the first match.

        :param fip_id: The floating IP id
        :type fip_id: string
        :returns: The NAT rule row or Load_Balancer row or None
        """


class SbAPI(api.API, metaclass=abc.ABCMeta):

    @abc.abstractmethod
    def chassis_exists(self, hostname):
        """Test if chassis for given hostname exists.

        @param hostname:       The hostname of the chassis
        @type hostname:        string
        :returns:              True if the chassis exists, else False.
        """

    @abc.abstractmethod
    def get_chassis_hostname_and_physnets(self):
        """Return a dict contains hostname and physnets mapping.

        Hostname will be dict key, and a list of physnets will be dict
        value. And hostname and physnets are related to the same host.
        """

    @abc.abstractmethod
    def get_gateway_chassis_from_cms_options(self, name_only=True):
        """Get chassis eligible for external connectivity from CMS options.

        When admin wants to enable router gateway on few chassis,
        he would set the external_ids as

        ovs-vsctl set open .
           external_ids:ovn-cms-options="enable-chassis-as-gw"
        In this function, we parse ovn-cms-options and return these chassis

        :param name_only: Return only the chassis names instead of
                          objects. Defaults to True.
        :returns: List with chassis.
        """

    @abc.abstractmethod
    def get_chassis_and_physnets(self):
        """Return a dict contains chassis name and physnets mapping.

        Chassis name will be dict key, and a list of physnets will be dict
        value. And chassis name and physnets are related to the same chassis.
        """

    @abc.abstractmethod
    def get_all_chassis(self, chassis_type=None):
        """Return a list of all chassis which match the compute_type

        :param chassis_type:    The type of chassis
        :type chassis_type:     string
        """

    @abc.abstractmethod
    def get_chassis_data_for_ml2_bind_port(self, hostname):
        """Return chassis data for ML2 port binding.

        @param hostname:       The hostname of the chassis
        @type hostname:        string
        :returns:              Tuple containing the chassis datapath type,
                               iface types and physical networks for the
                               OVN bridge mappings.
        :raises:               RuntimeError exception if an OVN chassis
                               does not exist.
        """