This file is indexed.

/usr/lib/python2.7/dist-packages/networking_arista/ml2/rpc/base.py is in python-networking-arista 2017.2.2-2ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
# Copyright (c) 2014 OpenStack Foundation
#
# 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
import base64
import os

from neutron_lib.db import api as db_api
from oslo_config import cfg
from oslo_log import log as logging
import six

from neutron.db.models.plugins.ml2 import vlanallocation

from networking_arista._i18n import _, _LW
from networking_arista.common import exceptions as arista_exc
from networking_arista.ml2 import arista_sec_gp

LOG = logging.getLogger(__name__)


@six.add_metaclass(abc.ABCMeta)
class AristaRPCWrapperBase(object):
    """Wraps Arista JSON RPC.

    All communications between Neutron and EOS are over JSON RPC.
    EOS - operating system used on Arista hardware
    Command API - JSON RPC API provided by Arista EOS
    """
    def __init__(self, neutron_db):
        self._ndb = neutron_db
        self._validate_config()
        self._server_ip = None
        self.region = cfg.CONF.ml2_arista.region_name
        self.sync_interval = cfg.CONF.ml2_arista.sync_interval
        self.conn_timeout = cfg.CONF.ml2_arista.conn_timeout
        self.eapi_hosts = cfg.CONF.ml2_arista.eapi_host.split(',')
        self.security_group_driver = arista_sec_gp.AristaSecGroupSwitchDriver(
            self._ndb)

        # We denote mlag_pair physnets as peer1_peer2 in the physnet name, the
        # following builds a mapping of peer name to physnet name for use
        # during port binding
        self.mlag_pairs = {}
        session = db_api.get_reader_session()
        with session.begin():
            physnets = session.query(
                vlanallocation.VlanAllocation.physical_network
            ).distinct().all()
        for (physnet,) in physnets:
            if '_' in physnet:
                peers = physnet.split('_')
                self.mlag_pairs[peers[0]] = physnet
                self.mlag_pairs[peers[1]] = physnet

        # Indication of CVX availabililty in the driver.
        self._cvx_available = True

        # Reference to SyncService object which is set in AristaDriver
        self.sync_service = None

    def _validate_config(self):
        if cfg.CONF.ml2_arista.get('eapi_host') == '':
            msg = _('Required option eapi_host is not set')
            LOG.error(msg)
            raise arista_exc.AristaConfigError(msg=msg)
        if cfg.CONF.ml2_arista.get('eapi_username') == '':
            msg = _('Required option eapi_username is not set')
            LOG.error(msg)
            raise arista_exc.AristaConfigError(msg=msg)

    def _api_username(self):
        return cfg.CONF.ml2_arista.eapi_username

    def _api_password(self):
        return cfg.CONF.ml2_arista.eapi_password

    def _get_random_name(self, length=10):
        """Returns a base64 encoded name."""
        result = base64.b64encode(os.urandom(10)).translate(None, b'=+/')

        return result if six.PY2 else result.decode('utf-8')

    def _get_cvx_hosts(self):
        cvx = []
        if self._server_ip:
            # If we know the master's IP, let's start with that
            cvx.append(self._server_ip)

        for h in self.eapi_hosts:
            if h.strip() not in cvx:
                cvx.append(h.strip())

        return cvx

    def set_cvx_unavailable(self):
        self._cvx_available = False
        if self.sync_service:
            self.sync_service.force_sync()

    def set_cvx_available(self):
        self._cvx_available = True

    def cvx_available(self):
        return self._cvx_available

    def check_cvx_availability(self):
        try:
            if self._get_eos_master():
                self.set_cvx_available()
                return True
        except Exception as exc:
            LOG.warning(_LW('%s when getting CVX master'), exc)

        self.set_cvx_unavailable()
        return False

    def delete_tenant(self, tenant_id):
        """Deletes a given tenant and all its networks and VMs from EOS.

        :param tenant_id: globally unique neutron tenant identifier
        """
        self.delete_tenant_bulk([tenant_id])

    def clear_region_updated_time(self):
        # TODO(shashank): Remove this once the call is removed from the ML2
        # driver.
        pass

    def create_network(self, tenant_id, network):
        """Creates a single network on Arista hardware

        :param tenant_id: globally unique neutron tenant identifier
        :param network: dict containing network_id, network_name and
                        segmentation_id
        """
        self.create_network_bulk(tenant_id, [network])

    def delete_network(self, tenant_id, network_id, network_segments):
        """Deletes a specified network for a given tenant

        :param tenant_id: globally unique neutron tenant identifier
        :param network_id: globally unique neutron network identifier
        :param network_segments: segments associated with the network
        """
        segments_info = []
        segments_info.extend({'id': segment['id'], 'network_id': network_id}
                             for segment in network_segments)
        self.delete_network_segments(tenant_id, segments_info)
        self.delete_network_bulk(tenant_id, [network_id])

    def delete_vm(self, tenant_id, vm_id):
        """Deletes a VM from EOS for a given tenant

        :param tenant_id : globally unique neutron tenant identifier
        :param vm_id : id of a VM that needs to be deleted.
        """
        self.delete_vm_bulk(tenant_id, [vm_id])

    @abc.abstractmethod
    def plug_port_into_network(self, device_id, host_id, port_id,
                               net_id, tenant_id, port_name, device_owner,
                               sg, orig_sg, vnic_type, segments=None,
                               switch_bindings=None, trunk_details=None):
        """Generic routine plug a port of a VM instace into network.

        :param device_id: globally unique identifier for the device
        :param host: ID of the host where the port is placed
        :param port_id: globally unique port ID that connects port to network
        :param network_id: globally unique neutron network identifier
        :param tenant_id: globally unique neutron tenant identifier
        :param port_name: Name of the port - for display purposes
        :param device_owner: Device owner - e.g. compute or network:dhcp
        :param sg: current security group for the port
        :param orig_sg: original security group for the port
        :param vnic_type: VNIC type for the port
        :param segments: list of network segments the port is bound to
        :param switch_bindings: List of switch_bindings
        :param trunk_details: List of subports of a trunk port
        """

    @abc.abstractmethod
    def unplug_port_from_network(self, device_id, device_owner, hostname,
                                 port_id, network_id, tenant_id, sg, vnic_type,
                                 switch_bindings=None, trunk_details=None):
        """Removes a port from the device

        :param device_id: globally unique identifier for the device
        :param host: ID of the host where the device is placed
        :param port_id: globally unique port ID that connects device to network
        :param network_id: globally unique neutron network identifier
        :param tenant_id: globally unique neutron tenant identifier
        :param trunk_details: List of subports of a trunk port
        """

    def _clean_acls(self, sg, failed_switch, switches_to_clean):
        """This is a helper function to clean up ACLs on switches.

        This called from within an exception - when apply_acl fails.
        Therefore, ensure that exception is raised after the cleanup
        is done.
        :param sg: Security Group to be removed
        :param failed_switch: IP of the switch where ACL failed
        :param switches_to_clean: List of switches containing link info
        """
        if not switches_to_clean:
            # This means the no switch needs cleaning - so, simply raise the
            # the exception and bail out
            msg = (_("Failed to apply ACL %(sg)s on switch %(switch)s") %
                   {'sg': sg, 'switch': failed_switch})
            LOG.error(msg)

        for s in switches_to_clean:
            try:
                # Port is being updated to remove security groups
                self.security_group_driver.remove_acl(sg,
                                                      s['switch_id'],
                                                      s['port_id'],
                                                      s['switch_info'])
            except Exception:
                msg = (_("Failed to remove ACL %(sg)s on switch %(switch)%") %
                       {'sg': sg, 'switch': s['switch_info']})
                LOG.warning(msg)
        raise arista_exc.AristaSecurityGroupError(msg=msg)

    def create_acl(self, sg):
        """Creates an ACL on Arista Switch.

        Deals with multiple configurations - such as multiple switches
        """
        self.security_group_driver.create_acl(sg)

    def delete_acl(self, sg):
        """Deletes an ACL from Arista Switch.

        Deals with multiple configurations - such as multiple switches
        """
        self.security_group_driver.delete_acl(sg)

    def create_acl_rule(self, sgr):
        """Creates an ACL on Arista Switch.

        For a given Security Group (ACL), it adds additional rule
        Deals with multiple configurations - such as multiple switches
        """
        self.security_group_driver.create_acl_rule(sgr)

    def delete_acl_rule(self, sgr):
        """Deletes an ACL rule on Arista Switch.

        For a given Security Group (ACL), it removes a rule
        Deals with multiple configurations - such as multiple switches
        """
        self.security_group_driver.delete_acl_rule(sgr)

    def perform_sync_of_sg(self):
        """Perform sync of the security groups between ML2 and EOS.

        This is unconditional sync to ensure that all security
        ACLs are pushed to all the switches, in case of switch
        or neutron reboot
        """
        self.security_group_driver.perform_sync_of_sg()

    @abc.abstractmethod
    def sync_supported(self):
        """Whether the EOS version supports sync.

        Returns True if sync is supported, false otherwise.
        """

    @abc.abstractmethod
    def bm_and_dvr_supported(self):
        """Whether EOS supports Ironic and DVR.

        Returns True if supported, false otherwise.
        """

    @abc.abstractmethod
    def register_with_eos(self, sync=False):
        """This is the registration request with EOS.

        This the initial handshake between Neutron and EOS.
        critical end-point information is registered with EOS.

        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def check_supported_features(self):
        """Checks whether the CLI commands are valid.

           This method tries to execute the commands on EOS and if it succeedes
           the command is stored.
        """

    @abc.abstractmethod
    def get_region_updated_time(self):
        """Return the timestamp of the last update.

           This method returns the time at which any entities in the region
           were updated.
        """

    @abc.abstractmethod
    def delete_this_region(self):
        """Deleted the region data from EOS."""

    @abc.abstractmethod
    def sync_start(self):
        """Let EOS know that a sync in being initiated."""

    @abc.abstractmethod
    def sync_end(self):
        """Let EOS know that sync is complete."""

    @abc.abstractmethod
    def get_tenants(self):
        """Returns dict of all tenants known by EOS.

        :returns: dictionary containing the networks per tenant
                  and VMs allocated per tenant
        """

    @abc.abstractmethod
    def delete_tenant_bulk(self, tenant_list, sync=False):
        """Sends a bulk request to delete the tenants.

        :param tenant_list: list of globaly unique neutron tenant ids which
                            need to be deleted.
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def create_network_bulk(self, tenant_id, network_list, sync=False):
        """Creates a network on Arista Hardware

        :param tenant_id: globally unique neutron tenant identifier
        :param network_list: list of dicts containing network_id, network_name
                             and segmentation_id
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def create_network_segments(self, tenant_id, network_id,
                                network_name, segments):
        """Creates a network on Arista Hardware

        Note: This method is not used at the moment. create_network()
        is used instead. This will be used once the support for
        multiple segments is added in Neutron.

        :param tenant_id: globally unique neutron tenant identifier
        :param network_id: globally unique neutron network identifier
        :param network_name: Network name - for display purposes
        :param segments: List of segments in a given network
        """

    @abc.abstractmethod
    def delete_network_bulk(self, tenant_id, network_id_list, sync=False):
        """Deletes the network ids specified for a tenant

        :param tenant_id: globally unique neutron tenant identifier
        :param network_id_list: list of globally unique neutron network
                                identifiers
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def delete_network_segments(self, tenant_id, network_segments):
        """Deletes the network segments

        :param network_segments: List of network segments to be delted.
        """

    @abc.abstractmethod
    def create_instance_bulk(self, tenant_id, neutron_ports, vms,
                             port_profiles, sync=False):
        """Sends a bulk request to create ports.

        :param tenant_id: globaly unique neutron tenant identifier
        :param neutron_ports: list of ports that need to be created.
        :param vms: list of vms to which the ports will be attached to.
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def delete_instance_bulk(self, tenant_id, instance_id_list, instance_type,
                             sync=False):
        """Deletes instances from EOS for a given tenant

        :param tenant_id : globally unique neutron tenant identifier
        :param instance_id_list : ids of instances that needs to be deleted.
        :param instance_type: The type of the instance which is being deleted.
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def delete_vm_bulk(self, tenant_id, vm_id_list, sync=False):
        """Deletes VMs from EOS for a given tenant

        :param tenant_id : globally unique neutron tenant identifier
        :param vm_id_list : ids of VMs that needs to be deleted.
        :param sync: This flags indicates that the region is being synced.
        """

    @abc.abstractmethod
    def hpb_supported(self):
        """Whether hierarchical port binding (HPB) is supported by CVX.

        Returns True if HPB is supported, False otherwise.
        """

    def apply_security_group(self, security_group, switch_bindings):
        """Applies ACLs on switch interface.

        Translates neutron security group to switch ACL and applies the ACLs
        on all the switch interfaces defined in the switch_bindings.

        :param security_group: Neutron security group
        :param switch_bindings: Switch link information
        """
        switches_with_acl = []
        for binding in switch_bindings:
            try:
                self.security_group_driver.apply_acl(security_group,
                                                     binding['switch_id'],
                                                     binding['port_id'],
                                                     binding['switch_info'])
                switches_with_acl.append(binding)
            except Exception:
                message = _LW('Unable to apply security group on %s') % (
                    binding['switch_id'])
                LOG.warning(message)
                self._clean_acls(security_group, binding['switch_id'],
                                 switches_with_acl)

    def remove_security_group(self, security_group, switch_bindings):
        """Removes ACLs from switch interface

        Translates neutron security group to switch ACL and removes the ACLs
        from all the switch interfaces defined in the switch_bindings.

        :param security_group: Neutron security group
        :param switch_bindings: Switch link information
        """
        for binding in switch_bindings:
            try:
                self.security_group_driver.remove_acl(security_group,
                                                      binding['switch_id'],
                                                      binding['port_id'],
                                                      binding['switch_info'])
            except Exception:
                message = _LW('Unable to remove security group from %s') % (
                    binding['switch_id'])
                LOG.warning(message)