/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)
|