1# Licensed under the Apache License, Version 2.0 (the "License"); 2# you may not use this file except in compliance with the License. 3# You may obtain a copy of the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, 9# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10# See the License for the specific language governing permissions and 11# limitations under the License. 12 13# import types so that we can reference ListType in sphinx param declarations. 14# We can't just use list, because sphinx gets confused by 15# openstack.resource.Resource.list and openstack.resource2.Resource.list 16import threading 17import time 18import types # noqa 19 20from openstack.cloud import _normalize 21from openstack.cloud import _utils 22from openstack.cloud import exc 23from openstack import exceptions 24from openstack import proxy 25 26 27class NetworkCloudMixin(_normalize.Normalizer): 28 29 def __init__(self): 30 self._ports = None 31 self._ports_time = 0 32 self._ports_lock = threading.Lock() 33 34 @_utils.cache_on_arguments() 35 def _neutron_extensions(self): 36 extensions = set() 37 for extension in self.network.extensions(): 38 extensions.add(extension['alias']) 39 return extensions 40 41 def _has_neutron_extension(self, extension_alias): 42 return extension_alias in self._neutron_extensions() 43 44 def search_networks(self, name_or_id=None, filters=None): 45 """Search networks 46 47 :param name_or_id: Name or ID of the desired network. 48 :param filters: a dict containing additional filters to use. e.g. 49 {'router:external': True} 50 51 :returns: a list of ``munch.Munch`` containing the network description. 52 53 :raises: ``OpenStackCloudException`` if something goes wrong during the 54 OpenStack API call. 55 """ 56 networks = self.list_networks( 57 filters if isinstance(filters, dict) else None) 58 return _utils._filter_list(networks, name_or_id, filters) 59 60 def search_routers(self, name_or_id=None, filters=None): 61 """Search routers 62 63 :param name_or_id: Name or ID of the desired router. 64 :param filters: a dict containing additional filters to use. e.g. 65 {'admin_state_up': True} 66 67 :returns: a list of ``munch.Munch`` containing the router description. 68 69 :raises: ``OpenStackCloudException`` if something goes wrong during the 70 OpenStack API call. 71 """ 72 routers = self.list_routers( 73 filters if isinstance(filters, dict) else None) 74 return _utils._filter_list(routers, name_or_id, filters) 75 76 def search_subnets(self, name_or_id=None, filters=None): 77 """Search subnets 78 79 :param name_or_id: Name or ID of the desired subnet. 80 :param filters: a dict containing additional filters to use. e.g. 81 {'enable_dhcp': True} 82 83 :returns: a list of ``munch.Munch`` containing the subnet description. 84 85 :raises: ``OpenStackCloudException`` if something goes wrong during the 86 OpenStack API call. 87 """ 88 subnets = self.list_subnets( 89 filters if isinstance(filters, dict) else None) 90 return _utils._filter_list(subnets, name_or_id, filters) 91 92 def search_ports(self, name_or_id=None, filters=None): 93 """Search ports 94 95 :param name_or_id: Name or ID of the desired port. 96 :param filters: a dict containing additional filters to use. e.g. 97 {'device_id': '2711c67a-b4a7-43dd-ace7-6187b791c3f0'} 98 99 :returns: a list of ``munch.Munch`` containing the port description. 100 101 :raises: ``OpenStackCloudException`` if something goes wrong during the 102 OpenStack API call. 103 """ 104 # If port caching is enabled, do not push the filter down to 105 # neutron; get all the ports (potentially from the cache) and 106 # filter locally. 107 if self._PORT_AGE or isinstance(filters, str): 108 pushdown_filters = None 109 else: 110 pushdown_filters = filters 111 ports = self.list_ports(pushdown_filters) 112 return _utils._filter_list(ports, name_or_id, filters) 113 114 def list_networks(self, filters=None): 115 """List all available networks. 116 117 :param filters: (optional) dict of filter conditions to push down 118 :returns: A list of ``munch.Munch`` containing network info. 119 120 """ 121 # If the cloud is running nova-network, just return an empty list. 122 if not self.has_service('network'): 123 return [] 124 # Translate None from search interface to empty {} for kwargs below 125 if not filters: 126 filters = {} 127 data = self.network.get("/networks", params=filters) 128 return self._get_and_munchify('networks', data) 129 130 def list_routers(self, filters=None): 131 """List all available routers. 132 133 :param filters: (optional) dict of filter conditions to push down 134 :returns: A list of router ``munch.Munch``. 135 136 """ 137 # If the cloud is running nova-network, just return an empty list. 138 if not self.has_service('network'): 139 return [] 140 # Translate None from search interface to empty {} for kwargs below 141 if not filters: 142 filters = {} 143 resp = self.network.get("/routers", params=filters) 144 data = proxy._json_response( 145 resp, 146 error_message="Error fetching router list") 147 return self._get_and_munchify('routers', data) 148 149 def list_subnets(self, filters=None): 150 """List all available subnets. 151 152 :param filters: (optional) dict of filter conditions to push down 153 :returns: A list of subnet ``munch.Munch``. 154 155 """ 156 # If the cloud is running nova-network, just return an empty list. 157 if not self.has_service('network'): 158 return [] 159 # Translate None from search interface to empty {} for kwargs below 160 if not filters: 161 filters = {} 162 data = self.network.get("/subnets", params=filters) 163 return self._get_and_munchify('subnets', data) 164 165 def list_ports(self, filters=None): 166 """List all available ports. 167 168 :param filters: (optional) dict of filter conditions to push down 169 :returns: A list of port ``munch.Munch``. 170 171 """ 172 # If pushdown filters are specified and we do not have batched caching 173 # enabled, bypass local caching and push down the filters. 174 if filters and self._PORT_AGE == 0: 175 return self._list_ports(filters) 176 177 if (time.time() - self._ports_time) >= self._PORT_AGE: 178 # Since we're using cached data anyway, we don't need to 179 # have more than one thread actually submit the list 180 # ports task. Let the first one submit it while holding 181 # a lock, and the non-blocking acquire method will cause 182 # subsequent threads to just skip this and use the old 183 # data until it succeeds. 184 # Initially when we never got data, block to retrieve some data. 185 first_run = self._ports is None 186 if self._ports_lock.acquire(first_run): 187 try: 188 if not (first_run and self._ports is not None): 189 self._ports = self._list_ports({}) 190 self._ports_time = time.time() 191 finally: 192 self._ports_lock.release() 193 # Wrap the return with filter_list so that if filters were passed 194 # but we were batching/caching and thus always fetching the whole 195 # list from the cloud, we still return a filtered list. 196 return _utils._filter_list(self._ports, None, filters or {}) 197 198 def _list_ports(self, filters): 199 # If the cloud is running nova-network, just return an empty list. 200 if not self.has_service('network'): 201 return [] 202 resp = self.network.get("/ports", params=filters) 203 data = proxy._json_response( 204 resp, 205 error_message="Error fetching port list") 206 return self._get_and_munchify('ports', data) 207 208 def get_qos_policy(self, name_or_id, filters=None): 209 """Get a QoS policy by name or ID. 210 211 :param name_or_id: Name or ID of the policy. 212 :param filters: 213 A dictionary of meta data to use for further filtering. Elements 214 of this dictionary may, themselves, be dictionaries. Example:: 215 216 { 217 'last_name': 'Smith', 218 'other': { 219 'gender': 'Female' 220 } 221 } 222 223 OR 224 A string containing a jmespath expression for further filtering. 225 Example:: "[?last_name==`Smith`] | [?other.gender]==`Female`]" 226 227 :returns: A policy ``munch.Munch`` or None if no matching network is 228 found. 229 230 """ 231 if not self._has_neutron_extension('qos'): 232 raise exc.OpenStackCloudUnavailableExtension( 233 'QoS extension is not available on target cloud') 234 if not filters: 235 filters = {} 236 return self.network.find_qos_policy( 237 name_or_id=name_or_id, 238 ignore_missing=True, 239 **filters) 240 241 def search_qos_policies(self, name_or_id=None, filters=None): 242 """Search QoS policies 243 244 :param name_or_id: Name or ID of the desired policy. 245 :param filters: a dict containing additional filters to use. e.g. 246 {'shared': True} 247 248 :returns: a list of ``munch.Munch`` containing the network description. 249 250 :raises: ``OpenStackCloudException`` if something goes wrong during the 251 OpenStack API call. 252 """ 253 if not self._has_neutron_extension('qos'): 254 raise exc.OpenStackCloudUnavailableExtension( 255 'QoS extension is not available on target cloud') 256 query = {} 257 if name_or_id: 258 query['name'] = name_or_id 259 if filters: 260 query.update(filters) 261 return list(self.network.qos_policies(**query)) 262 263 def list_qos_rule_types(self, filters=None): 264 """List all available QoS rule types. 265 266 :param filters: (optional) dict of filter conditions to push down 267 :returns: A list of rule types ``munch.Munch``. 268 269 """ 270 if not self._has_neutron_extension('qos'): 271 raise exc.OpenStackCloudUnavailableExtension( 272 'QoS extension is not available on target cloud') 273 274 # Translate None from search interface to empty {} for kwargs below 275 if not filters: 276 filters = {} 277 return list(self.network.qos_rule_types(**filters)) 278 279 def get_qos_rule_type_details(self, rule_type, filters=None): 280 """Get a QoS rule type details by rule type name. 281 282 :param string rule_type: Name of the QoS rule type. 283 284 :returns: A rule type details ``munch.Munch`` or None if 285 no matching rule type is found. 286 287 """ 288 if not self._has_neutron_extension('qos'): 289 raise exc.OpenStackCloudUnavailableExtension( 290 'QoS extension is not available on target cloud') 291 292 if not self._has_neutron_extension('qos-rule-type-details'): 293 raise exc.OpenStackCloudUnavailableExtension( 294 'qos-rule-type-details extension is not available ' 295 'on target cloud') 296 297 return self.network.get_qos_rule_type(rule_type) 298 299 def list_qos_policies(self, filters=None): 300 """List all available QoS policies. 301 302 :param filters: (optional) dict of filter conditions to push down 303 :returns: A list of policies ``munch.Munch``. 304 305 """ 306 if not self._has_neutron_extension('qos'): 307 raise exc.OpenStackCloudUnavailableExtension( 308 'QoS extension is not available on target cloud') 309 # Translate None from search interface to empty {} for kwargs below 310 if not filters: 311 filters = {} 312 return list(self.network.qos_policies(**filters)) 313 314 def get_network(self, name_or_id, filters=None): 315 """Get a network by name or ID. 316 317 :param name_or_id: Name or ID of the network. 318 :param filters: 319 A dictionary of meta data to use for further filtering. Elements 320 of this dictionary may, themselves, be dictionaries. Example:: 321 322 { 323 'last_name': 'Smith', 324 'other': { 325 'gender': 'Female' 326 } 327 } 328 329 OR 330 A string containing a jmespath expression for further filtering. 331 Example:: "[?last_name==`Smith`] | [?other.gender]==`Female`]" 332 333 :returns: A network ``munch.Munch`` or None if no matching network is 334 found. 335 336 """ 337 return _utils._get_entity(self, 'network', name_or_id, filters) 338 339 def get_network_by_id(self, id): 340 """ Get a network by ID 341 342 :param id: ID of the network. 343 :returns: A network ``munch.Munch``. 344 """ 345 resp = self.network.get('/networks/{id}'.format(id=id)) 346 data = proxy._json_response( 347 resp, 348 error_message="Error getting network with ID {id}".format(id=id) 349 ) 350 network = self._get_and_munchify('network', data) 351 352 return network 353 354 def get_router(self, name_or_id, filters=None): 355 """Get a router by name or ID. 356 357 :param name_or_id: Name or ID of the router. 358 :param filters: 359 A dictionary of meta data to use for further filtering. Elements 360 of this dictionary may, themselves, be dictionaries. Example:: 361 362 { 363 'last_name': 'Smith', 364 'other': { 365 'gender': 'Female' 366 } 367 } 368 369 OR 370 A string containing a jmespath expression for further filtering. 371 Example:: "[?last_name==`Smith`] | [?other.gender]==`Female`]" 372 373 :returns: A router ``munch.Munch`` or None if no matching router is 374 found. 375 376 """ 377 return _utils._get_entity(self, 'router', name_or_id, filters) 378 379 def get_subnet(self, name_or_id, filters=None): 380 """Get a subnet by name or ID. 381 382 :param name_or_id: Name or ID of the subnet. 383 :param filters: 384 A dictionary of meta data to use for further filtering. Elements 385 of this dictionary may, themselves, be dictionaries. Example:: 386 387 { 388 'last_name': 'Smith', 389 'other': { 390 'gender': 'Female' 391 } 392 } 393 394 :returns: A subnet ``munch.Munch`` or None if no matching subnet is 395 found. 396 397 """ 398 return _utils._get_entity(self, 'subnet', name_or_id, filters) 399 400 def get_subnet_by_id(self, id): 401 """ Get a subnet by ID 402 403 :param id: ID of the subnet. 404 :returns: A subnet ``munch.Munch``. 405 """ 406 resp = self.network.get('/subnets/{id}'.format(id=id)) 407 data = proxy._json_response( 408 resp, 409 error_message="Error getting subnet with ID {id}".format(id=id) 410 ) 411 subnet = self._get_and_munchify('subnet', data) 412 413 return subnet 414 415 def get_port(self, name_or_id, filters=None): 416 """Get a port by name or ID. 417 418 :param name_or_id: Name or ID of the port. 419 :param filters: 420 A dictionary of meta data to use for further filtering. Elements 421 of this dictionary may, themselves, be dictionaries. Example:: 422 423 { 424 'last_name': 'Smith', 425 'other': { 426 'gender': 'Female' 427 } 428 } 429 430 OR 431 A string containing a jmespath expression for further filtering. 432 Example:: "[?last_name==`Smith`] | [?other.gender]==`Female`]" 433 434 :returns: A port ``munch.Munch`` or None if no matching port is found. 435 436 """ 437 return _utils._get_entity(self, 'port', name_or_id, filters) 438 439 def get_port_by_id(self, id): 440 """ Get a port by ID 441 442 :param id: ID of the port. 443 :returns: A port ``munch.Munch``. 444 """ 445 resp = self.network.get('/ports/{id}'.format(id=id)) 446 data = proxy._json_response( 447 resp, 448 error_message="Error getting port with ID {id}".format(id=id) 449 ) 450 port = self._get_and_munchify('port', data) 451 452 return port 453 454 def create_network(self, name, shared=False, admin_state_up=True, 455 external=False, provider=None, project_id=None, 456 availability_zone_hints=None, 457 port_security_enabled=None, 458 mtu_size=None, dns_domain=None): 459 """Create a network. 460 461 :param string name: Name of the network being created. 462 :param bool shared: Set the network as shared. 463 :param bool admin_state_up: Set the network administrative state to up. 464 :param bool external: Whether this network is externally accessible. 465 :param dict provider: A dict of network provider options. Example:: 466 467 { 'network_type': 'vlan', 'segmentation_id': 'vlan1' } 468 :param string project_id: Specify the project ID this network 469 will be created on (admin-only). 470 :param types.ListType availability_zone_hints: A list of availability 471 zone hints. 472 :param bool port_security_enabled: Enable / Disable port security 473 :param int mtu_size: maximum transmission unit value to address 474 fragmentation. Minimum value is 68 for IPv4, and 1280 for IPv6. 475 :param string dns_domain: Specify the DNS domain associated with 476 this network. 477 478 :returns: The network object. 479 :raises: OpenStackCloudException on operation error. 480 """ 481 network = { 482 'name': name, 483 'admin_state_up': admin_state_up, 484 } 485 486 if shared: 487 network['shared'] = shared 488 489 if project_id is not None: 490 network['tenant_id'] = project_id 491 492 if availability_zone_hints is not None: 493 if not isinstance(availability_zone_hints, list): 494 raise exc.OpenStackCloudException( 495 "Parameter 'availability_zone_hints' must be a list") 496 if not self._has_neutron_extension('network_availability_zone'): 497 raise exc.OpenStackCloudUnavailableExtension( 498 'network_availability_zone extension is not available on ' 499 'target cloud') 500 network['availability_zone_hints'] = availability_zone_hints 501 502 if provider: 503 if not isinstance(provider, dict): 504 raise exc.OpenStackCloudException( 505 "Parameter 'provider' must be a dict") 506 # Only pass what we know 507 for attr in ('physical_network', 'network_type', 508 'segmentation_id'): 509 if attr in provider: 510 arg = "provider:" + attr 511 network[arg] = provider[attr] 512 513 # Do not send 'router:external' unless it is explicitly 514 # set since sending it *might* cause "Forbidden" errors in 515 # some situations. It defaults to False in the client, anyway. 516 if external: 517 network['router:external'] = True 518 519 if port_security_enabled is not None: 520 if not isinstance(port_security_enabled, bool): 521 raise exc.OpenStackCloudException( 522 "Parameter 'port_security_enabled' must be a bool") 523 network['port_security_enabled'] = port_security_enabled 524 525 if mtu_size: 526 if not isinstance(mtu_size, int): 527 raise exc.OpenStackCloudException( 528 "Parameter 'mtu_size' must be an integer.") 529 if not mtu_size >= 68: 530 raise exc.OpenStackCloudException( 531 "Parameter 'mtu_size' must be greater than 67.") 532 533 network['mtu'] = mtu_size 534 535 if dns_domain: 536 network['dns_domain'] = dns_domain 537 538 data = self.network.post("/networks", json={'network': network}) 539 540 # Reset cache so the new network is picked up 541 self._reset_network_caches() 542 return self._get_and_munchify('network', data) 543 544 @_utils.valid_kwargs("name", "shared", "admin_state_up", "external", 545 "provider", "mtu_size", "port_security_enabled", 546 "dns_domain") 547 def update_network(self, name_or_id, **kwargs): 548 """Update a network. 549 550 :param string name_or_id: Name or ID of the network being updated. 551 :param string name: New name of the network. 552 :param bool shared: Set the network as shared. 553 :param bool admin_state_up: Set the network administrative state to up. 554 :param bool external: Whether this network is externally accessible. 555 :param dict provider: A dict of network provider options. Example:: 556 557 { 'network_type': 'vlan', 'segmentation_id': 'vlan1' } 558 :param int mtu_size: New maximum transmission unit value to address 559 fragmentation. Minimum value is 68 for IPv4, and 1280 for IPv6. 560 :param bool port_security_enabled: Enable or disable port security. 561 :param string dns_domain: Specify the DNS domain associated with 562 this network. 563 564 :returns: The updated network object. 565 :raises: OpenStackCloudException on operation error. 566 """ 567 if 'provider' in kwargs: 568 if not isinstance(kwargs['provider'], dict): 569 raise exc.OpenStackCloudException( 570 "Parameter 'provider' must be a dict") 571 # Only pass what we know 572 provider = {} 573 for key in kwargs['provider']: 574 if key in ('physical_network', 'network_type', 575 'segmentation_id'): 576 provider['provider:' + key] = kwargs['provider'][key] 577 kwargs['provider'] = provider 578 579 if 'external' in kwargs: 580 kwargs['router:external'] = kwargs.pop('external') 581 582 if 'port_security_enabled' in kwargs: 583 if not isinstance(kwargs['port_security_enabled'], bool): 584 raise exc.OpenStackCloudException( 585 "Parameter 'port_security_enabled' must be a bool") 586 587 if 'mtu_size' in kwargs: 588 if not isinstance(kwargs['mtu_size'], int): 589 raise exc.OpenStackCloudException( 590 "Parameter 'mtu_size' must be an integer.") 591 if kwargs['mtu_size'] < 68: 592 raise exc.OpenStackCloudException( 593 "Parameter 'mtu_size' must be greater than 67.") 594 kwargs['mtu'] = kwargs.pop('mtu_size') 595 596 network = self.get_network(name_or_id) 597 if not network: 598 raise exc.OpenStackCloudException( 599 "Network %s not found." % name_or_id) 600 601 data = proxy._json_response(self.network.put( 602 "/networks/{net_id}".format(net_id=network.id), 603 json={"network": kwargs}), 604 error_message="Error updating network {0}".format(name_or_id)) 605 606 self._reset_network_caches() 607 608 return self._get_and_munchify('network', data) 609 610 def delete_network(self, name_or_id): 611 """Delete a network. 612 613 :param name_or_id: Name or ID of the network being deleted. 614 615 :returns: True if delete succeeded, False otherwise. 616 617 :raises: OpenStackCloudException on operation error. 618 """ 619 network = self.get_network(name_or_id) 620 if not network: 621 self.log.debug("Network %s not found for deleting", name_or_id) 622 return False 623 624 exceptions.raise_from_response(self.network.delete( 625 "/networks/{network_id}".format(network_id=network['id']))) 626 627 # Reset cache so the deleted network is removed 628 self._reset_network_caches() 629 630 return True 631 632 def set_network_quotas(self, name_or_id, **kwargs): 633 """ Set a network quota in a project 634 635 :param name_or_id: project name or id 636 :param kwargs: key/value pairs of quota name and quota value 637 638 :raises: OpenStackCloudException if the resource to set the 639 quota does not exist. 640 """ 641 642 proj = self.get_project(name_or_id) 643 if not proj: 644 raise exc.OpenStackCloudException("project does not exist") 645 646 exceptions.raise_from_response( 647 self.network.put( 648 '/quotas/{project_id}'.format(project_id=proj.id), 649 json={'quota': kwargs}), 650 error_message=("Error setting Neutron's quota for " 651 "project {0}".format(proj.id))) 652 653 def get_network_quotas(self, name_or_id, details=False): 654 """ Get network quotas for a project 655 656 :param name_or_id: project name or id 657 :param details: if set to True it will return details about usage 658 of quotas by given project 659 :raises: OpenStackCloudException if it's not a valid project 660 661 :returns: Munch object with the quotas 662 """ 663 proj = self.get_project(name_or_id) 664 if not proj: 665 raise exc.OpenStackCloudException("project does not exist") 666 url = '/quotas/{project_id}'.format(project_id=proj.id) 667 if details: 668 url = url + "/details" 669 data = proxy._json_response( 670 self.network.get(url), 671 error_message=("Error fetching Neutron's quota for " 672 "project {0}".format(proj.id))) 673 return self._get_and_munchify('quota', data) 674 675 def get_network_extensions(self): 676 """Get Cloud provided network extensions 677 678 :returns: set of Neutron extension aliases 679 """ 680 return self._neutron_extensions() 681 682 def delete_network_quotas(self, name_or_id): 683 """ Delete network quotas for a project 684 685 :param name_or_id: project name or id 686 :raises: OpenStackCloudException if it's not a valid project or the 687 network client call failed 688 689 :returns: dict with the quotas 690 """ 691 proj = self.get_project(name_or_id) 692 if not proj: 693 raise exc.OpenStackCloudException("project does not exist") 694 exceptions.raise_from_response( 695 self.network.delete( 696 '/quotas/{project_id}'.format(project_id=proj.id)), 697 error_message=("Error deleting Neutron's quota for " 698 "project {0}".format(proj.id))) 699 700 @_utils.valid_kwargs( 701 'action', 'description', 'destination_firewall_group_id', 702 'destination_ip_address', 'destination_port', 'enabled', 'ip_version', 703 'name', 'project_id', 'protocol', 'shared', 'source_firewall_group_id', 704 'source_ip_address', 'source_port') 705 def create_firewall_rule(self, **kwargs): 706 """ 707 Creates firewall rule. 708 709 :param action: Action performed on traffic. 710 Valid values: allow, deny 711 Defaults to deny. 712 :param description: Human-readable description. 713 :param destination_firewall_group_id: ID of destination firewall group. 714 :param destination_ip_address: IPv4-, IPv6 address or CIDR. 715 :param destination_port: Port or port range (e.g. 80:90) 716 :param bool enabled: Status of firewall rule. You can disable rules 717 without disassociating them from firewall 718 policies. Defaults to True. 719 :param int ip_version: IP Version. 720 Valid values: 4, 6 721 Defaults to 4. 722 :param name: Human-readable name. 723 :param project_id: Project id. 724 :param protocol: IP protocol. 725 Valid values: icmp, tcp, udp, null 726 :param bool shared: Visibility to other projects. 727 Defaults to False. 728 :param source_firewall_group_id: ID of source firewall group. 729 :param source_ip_address: IPv4-, IPv6 address or CIDR. 730 :param source_port: Port or port range (e.g. 80:90) 731 :raises: BadRequestException if parameters are malformed 732 :return: created firewall rule 733 :rtype: FirewallRule 734 """ 735 return self.network.create_firewall_rule(**kwargs) 736 737 def delete_firewall_rule(self, name_or_id, filters=None): 738 """ 739 Deletes firewall rule. 740 Prints debug message in case to-be-deleted resource was not found. 741 742 :param name_or_id: firewall rule name or id 743 :param dict filters: optional filters 744 :raises: DuplicateResource on multiple matches 745 :return: True if resource is successfully deleted, False otherwise. 746 :rtype: bool 747 """ 748 if not filters: 749 filters = {} 750 try: 751 firewall_rule = self.network.find_firewall_rule( 752 name_or_id, ignore_missing=False, **filters) 753 self.network.delete_firewall_rule(firewall_rule, 754 ignore_missing=False) 755 except exceptions.ResourceNotFound: 756 self.log.debug('Firewall rule %s not found for deleting', 757 name_or_id) 758 return False 759 return True 760 761 def get_firewall_rule(self, name_or_id, filters=None): 762 """ 763 Retrieves a single firewall rule. 764 765 :param name_or_id: firewall rule name or id 766 :param dict filters: optional filters 767 :raises: DuplicateResource on multiple matches 768 :return: firewall rule dict or None if not found 769 :rtype: FirewallRule 770 """ 771 if not filters: 772 filters = {} 773 return self.network.find_firewall_rule(name_or_id, **filters) 774 775 def list_firewall_rules(self, filters=None): 776 """ 777 Lists firewall rules. 778 779 :param dict filters: optional filters 780 :return: list of firewall rules 781 :rtype: list[FirewallRule] 782 """ 783 if not filters: 784 filters = {} 785 return list(self.network.firewall_rules(**filters)) 786 787 @_utils.valid_kwargs( 788 'action', 'description', 'destination_firewall_group_id', 789 'destination_ip_address', 'destination_port', 'enabled', 'ip_version', 790 'name', 'project_id', 'protocol', 'shared', 'source_firewall_group_id', 791 'source_ip_address', 'source_port') 792 def update_firewall_rule(self, name_or_id, filters=None, **kwargs): 793 """ 794 Updates firewall rule. 795 796 :param name_or_id: firewall rule name or id 797 :param dict filters: optional filters 798 :param kwargs: firewall rule update parameters. 799 See create_firewall_rule docstring for valid parameters. 800 :raises: BadRequestException if parameters are malformed 801 :raises: NotFoundException if resource is not found 802 :return: updated firewall rule 803 :rtype: FirewallRule 804 """ 805 if not filters: 806 filters = {} 807 firewall_rule = self.network.find_firewall_rule( 808 name_or_id, ignore_missing=False, **filters) 809 810 return self.network.update_firewall_rule(firewall_rule, **kwargs) 811 812 def _get_firewall_rule_ids(self, name_or_id_list, filters=None): 813 """ 814 Takes a list of firewall rule name or ids, looks them up and returns 815 a list of firewall rule ids. 816 817 Used by `create_firewall_policy` and `update_firewall_policy`. 818 819 :param list[str] name_or_id_list: firewall rule name or id list 820 :param dict filters: optional filters 821 :raises: DuplicateResource on multiple matches 822 :raises: NotFoundException if resource is not found 823 :return: list of firewall rule ids 824 :rtype: list[str] 825 """ 826 if not filters: 827 filters = {} 828 ids_list = [] 829 for name_or_id in name_or_id_list: 830 ids_list.append(self.network.find_firewall_rule( 831 name_or_id, ignore_missing=False, **filters)['id']) 832 return ids_list 833 834 @_utils.valid_kwargs('audited', 'description', 'firewall_rules', 'name', 835 'project_id', 'shared') 836 def create_firewall_policy(self, **kwargs): 837 """ 838 Create firewall policy. 839 840 :param bool audited: Status of audition of firewall policy. 841 Set to False each time the firewall policy or the 842 associated firewall rules are changed. 843 Has to be explicitly set to True. 844 :param description: Human-readable description. 845 :param list[str] firewall_rules: List of associated firewall rules. 846 :param name: Human-readable name. 847 :param project_id: Project id. 848 :param bool shared: Visibility to other projects. 849 Defaults to False. 850 :raises: BadRequestException if parameters are malformed 851 :raises: ResourceNotFound if a resource from firewall_list not found 852 :return: created firewall policy 853 :rtype: FirewallPolicy 854 """ 855 if 'firewall_rules' in kwargs: 856 kwargs['firewall_rules'] = self._get_firewall_rule_ids( 857 kwargs['firewall_rules']) 858 859 return self.network.create_firewall_policy(**kwargs) 860 861 def delete_firewall_policy(self, name_or_id, filters=None): 862 """ 863 Deletes firewall policy. 864 Prints debug message in case to-be-deleted resource was not found. 865 866 :param name_or_id: firewall policy name or id 867 :param dict filters: optional filters 868 :raises: DuplicateResource on multiple matches 869 :return: True if resource is successfully deleted, False otherwise. 870 :rtype: bool 871 """ 872 if not filters: 873 filters = {} 874 try: 875 firewall_policy = self.network.find_firewall_policy( 876 name_or_id, ignore_missing=False, **filters) 877 self.network.delete_firewall_policy(firewall_policy, 878 ignore_missing=False) 879 except exceptions.ResourceNotFound: 880 self.log.debug('Firewall policy %s not found for deleting', 881 name_or_id) 882 return False 883 return True 884 885 def get_firewall_policy(self, name_or_id, filters=None): 886 """ 887 Retrieves a single firewall policy. 888 889 :param name_or_id: firewall policy name or id 890 :param dict filters: optional filters 891 :raises: DuplicateResource on multiple matches 892 :return: firewall policy or None if not found 893 :rtype: FirewallPolicy 894 """ 895 if not filters: 896 filters = {} 897 return self.network.find_firewall_policy(name_or_id, **filters) 898 899 def list_firewall_policies(self, filters=None): 900 """ 901 Lists firewall policies. 902 903 :param dict filters: optional filters 904 :return: list of firewall policies 905 :rtype: list[FirewallPolicy] 906 """ 907 if not filters: 908 filters = {} 909 return list(self.network.firewall_policies(**filters)) 910 911 @_utils.valid_kwargs('audited', 'description', 'firewall_rules', 'name', 912 'project_id', 'shared') 913 def update_firewall_policy(self, name_or_id, filters=None, **kwargs): 914 """ 915 Updates firewall policy. 916 917 :param name_or_id: firewall policy name or id 918 :param dict filters: optional filters 919 :param kwargs: firewall policy update parameters 920 See create_firewall_policy docstring for valid parameters. 921 :raises: BadRequestException if parameters are malformed 922 :raises: DuplicateResource on multiple matches 923 :raises: ResourceNotFound if resource is not found 924 :return: updated firewall policy 925 :rtype: FirewallPolicy 926 """ 927 if not filters: 928 filters = {} 929 firewall_policy = self.network.find_firewall_policy( 930 name_or_id, ignore_missing=False, **filters) 931 932 if 'firewall_rules' in kwargs: 933 kwargs['firewall_rules'] = self._get_firewall_rule_ids( 934 kwargs['firewall_rules']) 935 936 return self.network.update_firewall_policy(firewall_policy, **kwargs) 937 938 def insert_rule_into_policy(self, name_or_id, rule_name_or_id, 939 insert_after=None, insert_before=None, 940 filters=None): 941 """ 942 Adds firewall rule to the firewall_rules list of a firewall policy. 943 Short-circuits and returns the firewall policy early if the firewall 944 rule id is already present in the firewall_rules list. 945 This method doesn't do re-ordering. If you want to move a firewall rule 946 or down the list, you have to remove and re-add it. 947 948 :param name_or_id: firewall policy name or id 949 :param rule_name_or_id: firewall rule name or id 950 :param insert_after: rule name or id that should precede added rule 951 :param insert_before: rule name or id that should succeed added rule 952 :param dict filters: optional filters 953 :raises: DuplicateResource on multiple matches 954 :raises: ResourceNotFound if firewall policy or any of the firewall 955 rules (inserted, after, before) is not found. 956 :return: updated firewall policy 957 :rtype: FirewallPolicy 958 """ 959 if not filters: 960 filters = {} 961 firewall_policy = self.network.find_firewall_policy( 962 name_or_id, ignore_missing=False, **filters) 963 964 firewall_rule = self.network.find_firewall_rule( 965 rule_name_or_id, ignore_missing=False) 966 # short-circuit if rule already in firewall_rules list 967 # the API can't do any re-ordering of existing rules 968 if firewall_rule['id'] in firewall_policy['firewall_rules']: 969 self.log.debug( 970 'Firewall rule %s already associated with firewall policy %s', 971 rule_name_or_id, name_or_id) 972 return firewall_policy 973 974 pos_params = {} 975 if insert_after is not None: 976 pos_params['insert_after'] = self.network.find_firewall_rule( 977 insert_after, ignore_missing=False)['id'] 978 979 if insert_before is not None: 980 pos_params['insert_before'] = self.network.find_firewall_rule( 981 insert_before, ignore_missing=False)['id'] 982 983 return self.network.insert_rule_into_policy(firewall_policy['id'], 984 firewall_rule['id'], 985 **pos_params) 986 987 def remove_rule_from_policy(self, name_or_id, rule_name_or_id, 988 filters=None): 989 """ 990 Remove firewall rule from firewall policy's firewall_rules list. 991 Short-circuits and returns firewall policy early if firewall rule 992 is already absent from the firewall_rules list. 993 994 :param name_or_id: firewall policy name or id 995 :param rule_name_or_id: firewall rule name or id 996 :param dict filters: optional filters 997 :raises: DuplicateResource on multiple matches 998 :raises: ResourceNotFound if firewall policy is not found 999 :return: updated firewall policy 1000 :rtype: FirewallPolicy 1001 """ 1002 if not filters: 1003 filters = {} 1004 firewall_policy = self.network.find_firewall_policy( 1005 name_or_id, ignore_missing=False, **filters) 1006 1007 firewall_rule = self.network.find_firewall_rule(rule_name_or_id) 1008 if not firewall_rule: 1009 # short-circuit: if firewall rule is not found, 1010 # return current firewall policy 1011 self.log.debug('Firewall rule %s not found for removing', 1012 rule_name_or_id) 1013 return firewall_policy 1014 1015 if firewall_rule['id'] not in firewall_policy['firewall_rules']: 1016 # short-circuit: if firewall rule id is not associated, 1017 # log it to debug and return current firewall policy 1018 self.log.debug( 1019 'Firewall rule %s not associated with firewall policy %s', 1020 rule_name_or_id, name_or_id) 1021 return firewall_policy 1022 1023 return self.network.remove_rule_from_policy(firewall_policy['id'], 1024 firewall_rule['id']) 1025 1026 @_utils.valid_kwargs( 1027 'admin_state_up', 'description', 'egress_firewall_policy', 1028 'ingress_firewall_policy', 'name', 'ports', 'project_id', 'shared') 1029 def create_firewall_group(self, **kwargs): 1030 """ 1031 Creates firewall group. The keys egress_firewall_policy and 1032 ingress_firewall_policy are looked up and mapped as 1033 egress_firewall_policy_id and ingress_firewall_policy_id respectively. 1034 Port name or ids list is transformed to port ids list before the POST 1035 request. 1036 1037 :param bool admin_state_up: State of firewall group. 1038 Will block all traffic if set to False. 1039 Defaults to True. 1040 :param description: Human-readable description. 1041 :param egress_firewall_policy: Name or id of egress firewall policy. 1042 :param ingress_firewall_policy: Name or id of ingress firewall policy. 1043 :param name: Human-readable name. 1044 :param list[str] ports: List of associated ports (name or id) 1045 :param project_id: Project id. 1046 :param shared: Visibility to other projects. 1047 Defaults to False. 1048 :raises: BadRequestException if parameters are malformed 1049 :raises: DuplicateResource on multiple matches 1050 :raises: ResourceNotFound if (ingress-, egress-) firewall policy or 1051 a port is not found. 1052 :return: created firewall group 1053 :rtype: FirewallGroup 1054 """ 1055 self._lookup_ingress_egress_firewall_policy_ids(kwargs) 1056 if 'ports' in kwargs: 1057 kwargs['ports'] = self._get_port_ids(kwargs['ports']) 1058 return self.network.create_firewall_group(**kwargs) 1059 1060 def delete_firewall_group(self, name_or_id, filters=None): 1061 """ 1062 Deletes firewall group. 1063 Prints debug message in case to-be-deleted resource was not found. 1064 1065 :param name_or_id: firewall group name or id 1066 :param dict filters: optional filters 1067 :raises: DuplicateResource on multiple matches 1068 :return: True if resource is successfully deleted, False otherwise. 1069 :rtype: bool 1070 """ 1071 if not filters: 1072 filters = {} 1073 try: 1074 firewall_group = self.network.find_firewall_group( 1075 name_or_id, ignore_missing=False, **filters) 1076 self.network.delete_firewall_group(firewall_group, 1077 ignore_missing=False) 1078 except exceptions.ResourceNotFound: 1079 self.log.debug('Firewall group %s not found for deleting', 1080 name_or_id) 1081 return False 1082 return True 1083 1084 def get_firewall_group(self, name_or_id, filters=None): 1085 """ 1086 Retrieves firewall group. 1087 1088 :param name_or_id: firewall group name or id 1089 :param dict filters: optional filters 1090 :raises: DuplicateResource on multiple matches 1091 :return: firewall group or None if not found 1092 :rtype: FirewallGroup 1093 """ 1094 if not filters: 1095 filters = {} 1096 return self.network.find_firewall_group(name_or_id, **filters) 1097 1098 def list_firewall_groups(self, filters=None): 1099 """ 1100 Lists firewall groups. 1101 1102 :param dict filters: optional filters 1103 :return: list of firewall groups 1104 :rtype: list[FirewallGroup] 1105 """ 1106 if not filters: 1107 filters = {} 1108 return list(self.network.firewall_groups(**filters)) 1109 1110 @_utils.valid_kwargs( 1111 'admin_state_up', 'description', 'egress_firewall_policy', 1112 'ingress_firewall_policy', 'name', 'ports', 'project_id', 'shared') 1113 def update_firewall_group(self, name_or_id, filters=None, **kwargs): 1114 """ 1115 Updates firewall group. 1116 To unset egress- or ingress firewall policy, set egress_firewall_policy 1117 or ingress_firewall_policy to None. You can also set 1118 egress_firewall_policy_id and ingress_firewall_policy_id directly, 1119 which will skip the policy lookups. 1120 1121 :param name_or_id: firewall group name or id 1122 :param dict filters: optional filters 1123 :param kwargs: firewall group update parameters 1124 See create_firewall_group docstring for valid parameters. 1125 :raises: BadRequestException if parameters are malformed 1126 :raises: DuplicateResource on multiple matches 1127 :raises: ResourceNotFound if firewall group, a firewall policy 1128 (egress, ingress) or port is not found 1129 :return: updated firewall group 1130 :rtype: FirewallGroup 1131 """ 1132 if not filters: 1133 filters = {} 1134 firewall_group = self.network.find_firewall_group( 1135 name_or_id, ignore_missing=False, **filters) 1136 self._lookup_ingress_egress_firewall_policy_ids(kwargs) 1137 1138 if 'ports' in kwargs: 1139 kwargs['ports'] = self._get_port_ids(kwargs['ports']) 1140 return self.network.update_firewall_group(firewall_group, **kwargs) 1141 1142 def _lookup_ingress_egress_firewall_policy_ids(self, firewall_group): 1143 """ 1144 Transforms firewall_group dict IN-PLACE. Takes the value of the keys 1145 egress_firewall_policy and ingress_firewall_policy, looks up the 1146 policy ids and maps them to egress_firewall_policy_id and 1147 ingress_firewall_policy_id. Old keys which were used for the lookup 1148 are deleted. 1149 1150 :param dict firewall_group: firewall group dict 1151 :raises: DuplicateResource on multiple matches 1152 :raises: ResourceNotFound if a firewall policy is not found 1153 """ 1154 for key in ('egress_firewall_policy', 'ingress_firewall_policy'): 1155 if key not in firewall_group: 1156 continue 1157 if firewall_group[key] is None: 1158 val = None 1159 else: 1160 val = self.network.find_firewall_policy( 1161 firewall_group[key], ignore_missing=False)['id'] 1162 firewall_group[key + '_id'] = val 1163 del firewall_group[key] 1164 1165 @_utils.valid_kwargs("name", "description", "shared", "default", 1166 "project_id") 1167 def create_qos_policy(self, **kwargs): 1168 """Create a QoS policy. 1169 1170 :param string name: Name of the QoS policy being created. 1171 :param string description: Description of created QoS policy. 1172 :param bool shared: Set the QoS policy as shared. 1173 :param bool default: Set the QoS policy as default for project. 1174 :param string project_id: Specify the project ID this QoS policy 1175 will be created on (admin-only). 1176 1177 :returns: The QoS policy object. 1178 :raises: OpenStackCloudException on operation error. 1179 """ 1180 if not self._has_neutron_extension('qos'): 1181 raise exc.OpenStackCloudUnavailableExtension( 1182 'QoS extension is not available on target cloud') 1183 1184 default = kwargs.pop("default", None) 1185 if default is not None: 1186 if self._has_neutron_extension('qos-default'): 1187 kwargs['is_default'] = default 1188 else: 1189 self.log.debug("'qos-default' extension is not available on " 1190 "target cloud") 1191 1192 return self.network.create_qos_policy(**kwargs) 1193 1194 @_utils.valid_kwargs("name", "description", "shared", "default", 1195 "project_id") 1196 def update_qos_policy(self, name_or_id, **kwargs): 1197 """Update an existing QoS policy. 1198 1199 :param string name_or_id: 1200 Name or ID of the QoS policy to update. 1201 :param string policy_name: 1202 The new name of the QoS policy. 1203 :param string description: 1204 The new description of the QoS policy. 1205 :param bool shared: 1206 If True, the QoS policy will be set as shared. 1207 :param bool default: 1208 If True, the QoS policy will be set as default for project. 1209 1210 :returns: The updated QoS policy object. 1211 :raises: OpenStackCloudException on operation error. 1212 """ 1213 if not self._has_neutron_extension('qos'): 1214 raise exc.OpenStackCloudUnavailableExtension( 1215 'QoS extension is not available on target cloud') 1216 1217 default = kwargs.pop("default", None) 1218 if default is not None: 1219 if self._has_neutron_extension('qos-default'): 1220 kwargs['is_default'] = default 1221 else: 1222 self.log.debug("'qos-default' extension is not available on " 1223 "target cloud") 1224 1225 if not kwargs: 1226 self.log.debug("No QoS policy data to update") 1227 return 1228 1229 curr_policy = self.network.find_qos_policy(name_or_id) 1230 if not curr_policy: 1231 raise exc.OpenStackCloudException( 1232 "QoS policy %s not found." % name_or_id) 1233 return self.network.update_qos_policy(curr_policy, **kwargs) 1234 1235 def delete_qos_policy(self, name_or_id): 1236 """Delete a QoS policy. 1237 1238 :param name_or_id: Name or ID of the policy being deleted. 1239 1240 :returns: True if delete succeeded, False otherwise. 1241 1242 :raises: OpenStackCloudException on operation error. 1243 """ 1244 if not self._has_neutron_extension('qos'): 1245 raise exc.OpenStackCloudUnavailableExtension( 1246 'QoS extension is not available on target cloud') 1247 policy = self.network.find_qos_policy(name_or_id) 1248 if not policy: 1249 self.log.debug("QoS policy %s not found for deleting", name_or_id) 1250 return False 1251 self.network.delete_qos_policy(policy) 1252 1253 return True 1254 1255 def search_qos_bandwidth_limit_rules( 1256 self, policy_name_or_id, rule_id=None, filters=None 1257 ): 1258 """Search QoS bandwidth limit rules 1259 1260 :param string policy_name_or_id: Name or ID of the QoS policy to which 1261 rules should be associated. 1262 :param string rule_id: ID of searched rule. 1263 :param filters: a dict containing additional filters to use. e.g. 1264 {'max_kbps': 1000} 1265 1266 :returns: a list of ``munch.Munch`` containing the bandwidth limit 1267 rule descriptions. 1268 1269 :raises: ``OpenStackCloudException`` if something goes wrong during the 1270 OpenStack API call. 1271 """ 1272 rules = self.list_qos_bandwidth_limit_rules(policy_name_or_id, filters) 1273 return _utils._filter_list(rules, rule_id, filters) 1274 1275 def list_qos_bandwidth_limit_rules(self, policy_name_or_id, filters=None): 1276 """List all available QoS bandwidth limit rules. 1277 1278 :param string policy_name_or_id: Name or ID of the QoS policy from 1279 from rules should be listed. 1280 :param filters: (optional) dict of filter conditions to push down 1281 :returns: A list of ``munch.Munch`` containing rule info. 1282 1283 :raises: ``OpenStackCloudResourceNotFound`` if QoS policy will not be 1284 found. 1285 """ 1286 if not self._has_neutron_extension('qos'): 1287 raise exc.OpenStackCloudUnavailableExtension( 1288 'QoS extension is not available on target cloud') 1289 1290 policy = self.network.find_qos_policy(policy_name_or_id) 1291 if not policy: 1292 raise exc.OpenStackCloudResourceNotFound( 1293 "QoS policy {name_or_id} not Found.".format( 1294 name_or_id=policy_name_or_id)) 1295 1296 # Translate None from search interface to empty {} for kwargs below 1297 if not filters: 1298 filters = {} 1299 1300 return list(self.network.qos_bandwidth_limit_rules( 1301 qos_policy=policy, **filters)) 1302 1303 def get_qos_bandwidth_limit_rule(self, policy_name_or_id, rule_id): 1304 """Get a QoS bandwidth limit rule by name or ID. 1305 1306 :param string policy_name_or_id: Name or ID of the QoS policy to which 1307 rule should be associated. 1308 :param rule_id: ID of the rule. 1309 1310 :returns: A bandwidth limit rule ``munch.Munch`` or None if 1311 no matching rule is found. 1312 1313 """ 1314 if not self._has_neutron_extension('qos'): 1315 raise exc.OpenStackCloudUnavailableExtension( 1316 'QoS extension is not available on target cloud') 1317 1318 policy = self.network.find_qos_policy(policy_name_or_id) 1319 if not policy: 1320 raise exc.OpenStackCloudResourceNotFound( 1321 "QoS policy {name_or_id} not Found.".format( 1322 name_or_id=policy_name_or_id)) 1323 1324 return self.network.get_qos_bandwidth_limit_rule( 1325 rule_id, policy) 1326 1327 @_utils.valid_kwargs("max_burst_kbps", "direction") 1328 def create_qos_bandwidth_limit_rule(self, policy_name_or_id, max_kbps, 1329 **kwargs): 1330 """Create a QoS bandwidth limit rule. 1331 1332 :param string policy_name_or_id: Name or ID of the QoS policy to which 1333 rule should be associated. 1334 :param int max_kbps: Maximum bandwidth limit value 1335 (in kilobits per second). 1336 :param int max_burst_kbps: Maximum burst value (in kilobits). 1337 :param string direction: Ingress or egress. 1338 The direction in which the traffic will be limited. 1339 1340 :returns: The QoS bandwidth limit rule. 1341 :raises: OpenStackCloudException on operation error. 1342 """ 1343 if not self._has_neutron_extension('qos'): 1344 raise exc.OpenStackCloudUnavailableExtension( 1345 'QoS extension is not available on target cloud') 1346 1347 policy = self.network.find_qos_policy(policy_name_or_id) 1348 if not policy: 1349 raise exc.OpenStackCloudResourceNotFound( 1350 "QoS policy {name_or_id} not Found.".format( 1351 name_or_id=policy_name_or_id)) 1352 1353 if kwargs.get("direction") is not None: 1354 if not self._has_neutron_extension('qos-bw-limit-direction'): 1355 kwargs.pop("direction") 1356 self.log.debug( 1357 "'qos-bw-limit-direction' extension is not available on " 1358 "target cloud") 1359 1360 kwargs['max_kbps'] = max_kbps 1361 return self.network.create_qos_bandwidth_limit_rule(policy, **kwargs) 1362 1363 @_utils.valid_kwargs("max_kbps", "max_burst_kbps", "direction") 1364 def update_qos_bandwidth_limit_rule(self, policy_name_or_id, rule_id, 1365 **kwargs): 1366 """Update a QoS bandwidth limit rule. 1367 1368 :param string policy_name_or_id: Name or ID of the QoS policy to which 1369 rule is associated. 1370 :param string rule_id: ID of rule to update. 1371 :param int max_kbps: Maximum bandwidth limit value 1372 (in kilobits per second). 1373 :param int max_burst_kbps: Maximum burst value (in kilobits). 1374 :param string direction: Ingress or egress. 1375 The direction in which the traffic will be limited. 1376 1377 :returns: The updated QoS bandwidth limit rule. 1378 :raises: OpenStackCloudException on operation error. 1379 """ 1380 if not self._has_neutron_extension('qos'): 1381 raise exc.OpenStackCloudUnavailableExtension( 1382 'QoS extension is not available on target cloud') 1383 1384 policy = self.network.find_qos_policy( 1385 policy_name_or_id, 1386 ignore_missing=True) 1387 if not policy: 1388 raise exc.OpenStackCloudResourceNotFound( 1389 "QoS policy {name_or_id} not Found.".format( 1390 name_or_id=policy_name_or_id)) 1391 1392 if kwargs.get("direction") is not None: 1393 if not self._has_neutron_extension('qos-bw-limit-direction'): 1394 kwargs.pop("direction") 1395 self.log.debug( 1396 "'qos-bw-limit-direction' extension is not available on " 1397 "target cloud") 1398 1399 if not kwargs: 1400 self.log.debug("No QoS bandwidth limit rule data to update") 1401 return 1402 1403 curr_rule = self.network.get_qos_bandwidth_limit_rule( 1404 qos_rule=rule_id, qos_policy=policy) 1405 if not curr_rule: 1406 raise exc.OpenStackCloudException( 1407 "QoS bandwidth_limit_rule {rule_id} not found in policy " 1408 "{policy_id}".format(rule_id=rule_id, 1409 policy_id=policy['id'])) 1410 1411 return self.network.update_qos_bandwidth_limit_rule( 1412 qos_rule=curr_rule, qos_policy=policy, **kwargs) 1413 1414 def delete_qos_bandwidth_limit_rule(self, policy_name_or_id, rule_id): 1415 """Delete a QoS bandwidth limit rule. 1416 1417 :param string policy_name_or_id: Name or ID of the QoS policy to which 1418 rule is associated. 1419 :param string rule_id: ID of rule to update. 1420 1421 :raises: OpenStackCloudException on operation error. 1422 """ 1423 if not self._has_neutron_extension('qos'): 1424 raise exc.OpenStackCloudUnavailableExtension( 1425 'QoS extension is not available on target cloud') 1426 1427 policy = self.network.find_qos_policy(policy_name_or_id) 1428 if not policy: 1429 raise exc.OpenStackCloudResourceNotFound( 1430 "QoS policy {name_or_id} not Found.".format( 1431 name_or_id=policy_name_or_id)) 1432 1433 try: 1434 self.network.delete_qos_bandwidth_limit_rule( 1435 rule_id, policy, ignore_missing=False) 1436 except exceptions.ResourceNotFound: 1437 self.log.debug( 1438 "QoS bandwidth limit rule {rule_id} not found in policy " 1439 "{policy_id}. Ignoring.".format(rule_id=rule_id, 1440 policy_id=policy['id'])) 1441 return False 1442 1443 return True 1444 1445 def search_qos_dscp_marking_rules(self, policy_name_or_id, rule_id=None, 1446 filters=None): 1447 """Search QoS DSCP marking rules 1448 1449 :param string policy_name_or_id: Name or ID of the QoS policy to which 1450 rules should be associated. 1451 :param string rule_id: ID of searched rule. 1452 :param filters: a dict containing additional filters to use. e.g. 1453 {'dscp_mark': 32} 1454 1455 :returns: a list of ``munch.Munch`` containing the dscp marking 1456 rule descriptions. 1457 1458 :raises: ``OpenStackCloudException`` if something goes wrong during the 1459 OpenStack API call. 1460 """ 1461 rules = self.list_qos_dscp_marking_rules(policy_name_or_id, filters) 1462 return _utils._filter_list(rules, rule_id, filters) 1463 1464 def list_qos_dscp_marking_rules(self, policy_name_or_id, filters=None): 1465 """List all available QoS DSCP marking rules. 1466 1467 :param string policy_name_or_id: Name or ID of the QoS policy from 1468 from rules should be listed. 1469 :param filters: (optional) dict of filter conditions to push down 1470 :returns: A list of ``munch.Munch`` containing rule info. 1471 1472 :raises: ``OpenStackCloudResourceNotFound`` if QoS policy will not be 1473 found. 1474 """ 1475 if not self._has_neutron_extension('qos'): 1476 raise exc.OpenStackCloudUnavailableExtension( 1477 'QoS extension is not available on target cloud') 1478 1479 policy = self.network.find_qos_policy( 1480 policy_name_or_id, ignore_missing=True) 1481 if not policy: 1482 raise exc.OpenStackCloudResourceNotFound( 1483 "QoS policy {name_or_id} not Found.".format( 1484 name_or_id=policy_name_or_id)) 1485 1486 # Translate None from search interface to empty {} for kwargs below 1487 if not filters: 1488 filters = {} 1489 1490 return list(self.network.qos_dscp_marking_rules(policy, **filters)) 1491 1492 def get_qos_dscp_marking_rule(self, policy_name_or_id, rule_id): 1493 """Get a QoS DSCP marking rule by name or ID. 1494 1495 :param string policy_name_or_id: Name or ID of the QoS policy to which 1496 rule should be associated. 1497 :param rule_id: ID of the rule. 1498 1499 :returns: A bandwidth limit rule ``munch.Munch`` or None if 1500 no matching rule is found. 1501 1502 """ 1503 if not self._has_neutron_extension('qos'): 1504 raise exc.OpenStackCloudUnavailableExtension( 1505 'QoS extension is not available on target cloud') 1506 1507 policy = self.network.find_qos_policy(policy_name_or_id) 1508 if not policy: 1509 raise exc.OpenStackCloudResourceNotFound( 1510 "QoS policy {name_or_id} not Found.".format( 1511 name_or_id=policy_name_or_id)) 1512 1513 return self.network.get_qos_dscp_marking_rule(rule_id, policy) 1514 1515 def create_qos_dscp_marking_rule(self, policy_name_or_id, dscp_mark): 1516 """Create a QoS DSCP marking rule. 1517 1518 :param string policy_name_or_id: Name or ID of the QoS policy to which 1519 rule should be associated. 1520 :param int dscp_mark: DSCP mark value 1521 1522 :returns: The QoS DSCP marking rule. 1523 :raises: OpenStackCloudException on operation error. 1524 """ 1525 if not self._has_neutron_extension('qos'): 1526 raise exc.OpenStackCloudUnavailableExtension( 1527 'QoS extension is not available on target cloud') 1528 1529 policy = self.network.find_qos_policy(policy_name_or_id) 1530 if not policy: 1531 raise exc.OpenStackCloudResourceNotFound( 1532 "QoS policy {name_or_id} not Found.".format( 1533 name_or_id=policy_name_or_id)) 1534 1535 return self.network.create_qos_dscp_marking_rule( 1536 policy, dscp_mark=dscp_mark) 1537 1538 @_utils.valid_kwargs("dscp_mark") 1539 def update_qos_dscp_marking_rule(self, policy_name_or_id, rule_id, 1540 **kwargs): 1541 """Update a QoS DSCP marking rule. 1542 1543 :param string policy_name_or_id: Name or ID of the QoS policy to which 1544 rule is associated. 1545 :param string rule_id: ID of rule to update. 1546 :param int dscp_mark: DSCP mark value 1547 1548 :returns: The updated QoS bandwidth limit rule. 1549 :raises: OpenStackCloudException on operation error. 1550 """ 1551 if not self._has_neutron_extension('qos'): 1552 raise exc.OpenStackCloudUnavailableExtension( 1553 'QoS extension is not available on target cloud') 1554 1555 policy = self.network.find_qos_policy(policy_name_or_id) 1556 if not policy: 1557 raise exc.OpenStackCloudResourceNotFound( 1558 "QoS policy {name_or_id} not Found.".format( 1559 name_or_id=policy_name_or_id)) 1560 1561 if not kwargs: 1562 self.log.debug("No QoS DSCP marking rule data to update") 1563 return 1564 1565 curr_rule = self.network.get_qos_dscp_marking_rule( 1566 rule_id, policy) 1567 if not curr_rule: 1568 raise exc.OpenStackCloudException( 1569 "QoS dscp_marking_rule {rule_id} not found in policy " 1570 "{policy_id}".format(rule_id=rule_id, 1571 policy_id=policy['id'])) 1572 1573 return self.network.update_qos_dscp_marking_rule( 1574 curr_rule, policy, **kwargs) 1575 1576 def delete_qos_dscp_marking_rule(self, policy_name_or_id, rule_id): 1577 """Delete a QoS DSCP marking rule. 1578 1579 :param string policy_name_or_id: Name or ID of the QoS policy to which 1580 rule is associated. 1581 :param string rule_id: ID of rule to update. 1582 1583 :raises: OpenStackCloudException on operation error. 1584 """ 1585 if not self._has_neutron_extension('qos'): 1586 raise exc.OpenStackCloudUnavailableExtension( 1587 'QoS extension is not available on target cloud') 1588 1589 policy = self.network.find_qos_policy(policy_name_or_id) 1590 if not policy: 1591 raise exc.OpenStackCloudResourceNotFound( 1592 "QoS policy {name_or_id} not Found.".format( 1593 name_or_id=policy_name_or_id)) 1594 1595 try: 1596 self.network.delete_qos_dscp_marking_rule( 1597 rule_id, policy, ignore_missing=False) 1598 except exceptions.ResourceNotFound: 1599 self.log.debug( 1600 "QoS DSCP marking rule {rule_id} not found in policy " 1601 "{policy_id}. Ignoring.".format(rule_id=rule_id, 1602 policy_id=policy['id'])) 1603 return False 1604 1605 return True 1606 1607 def search_qos_minimum_bandwidth_rules(self, policy_name_or_id, 1608 rule_id=None, filters=None): 1609 """Search QoS minimum bandwidth rules 1610 1611 :param string policy_name_or_id: Name or ID of the QoS policy to which 1612 rules should be associated. 1613 :param string rule_id: ID of searched rule. 1614 :param filters: a dict containing additional filters to use. e.g. 1615 {'min_kbps': 1000} 1616 1617 :returns: a list of ``munch.Munch`` containing the bandwidth limit 1618 rule descriptions. 1619 1620 :raises: ``OpenStackCloudException`` if something goes wrong during the 1621 OpenStack API call. 1622 """ 1623 rules = self.list_qos_minimum_bandwidth_rules( 1624 policy_name_or_id, filters) 1625 return _utils._filter_list(rules, rule_id, filters) 1626 1627 def list_qos_minimum_bandwidth_rules(self, policy_name_or_id, 1628 filters=None): 1629 """List all available QoS minimum bandwidth rules. 1630 1631 :param string policy_name_or_id: Name or ID of the QoS policy from 1632 from rules should be listed. 1633 :param filters: (optional) dict of filter conditions to push down 1634 :returns: A list of ``munch.Munch`` containing rule info. 1635 1636 :raises: ``OpenStackCloudResourceNotFound`` if QoS policy will not be 1637 found. 1638 """ 1639 if not self._has_neutron_extension('qos'): 1640 raise exc.OpenStackCloudUnavailableExtension( 1641 'QoS extension is not available on target cloud') 1642 1643 policy = self.network.find_qos_policy(policy_name_or_id) 1644 if not policy: 1645 raise exc.OpenStackCloudResourceNotFound( 1646 "QoS policy {name_or_id} not Found.".format( 1647 name_or_id=policy_name_or_id)) 1648 1649 # Translate None from search interface to empty {} for kwargs below 1650 if not filters: 1651 filters = {} 1652 1653 return list( 1654 self.network.qos_minimum_bandwidth_rules( 1655 policy, **filters)) 1656 1657 def get_qos_minimum_bandwidth_rule(self, policy_name_or_id, rule_id): 1658 """Get a QoS minimum bandwidth rule by name or ID. 1659 1660 :param string policy_name_or_id: Name or ID of the QoS policy to which 1661 rule should be associated. 1662 :param rule_id: ID of the rule. 1663 1664 :returns: A bandwidth limit rule ``munch.Munch`` or None if 1665 no matching rule is found. 1666 1667 """ 1668 if not self._has_neutron_extension('qos'): 1669 raise exc.OpenStackCloudUnavailableExtension( 1670 'QoS extension is not available on target cloud') 1671 1672 policy = self.network.find_qos_policy(policy_name_or_id) 1673 if not policy: 1674 raise exc.OpenStackCloudResourceNotFound( 1675 "QoS policy {name_or_id} not Found.".format( 1676 name_or_id=policy_name_or_id)) 1677 1678 return self.network.get_qos_minimum_bandwidth_rule(rule_id, policy) 1679 1680 @_utils.valid_kwargs("direction") 1681 def create_qos_minimum_bandwidth_rule( 1682 self, policy_name_or_id, min_kbps, **kwargs 1683 ): 1684 """Create a QoS minimum bandwidth limit rule. 1685 1686 :param string policy_name_or_id: Name or ID of the QoS policy to which 1687 rule should be associated. 1688 :param int min_kbps: Minimum bandwidth value (in kilobits per second). 1689 :param string direction: Ingress or egress. 1690 The direction in which the traffic will be available. 1691 1692 :returns: The QoS minimum bandwidth rule. 1693 :raises: OpenStackCloudException on operation error. 1694 """ 1695 if not self._has_neutron_extension('qos'): 1696 raise exc.OpenStackCloudUnavailableExtension( 1697 'QoS extension is not available on target cloud') 1698 1699 policy = self.network.find_qos_policy(policy_name_or_id) 1700 if not policy: 1701 raise exc.OpenStackCloudResourceNotFound( 1702 "QoS policy {name_or_id} not Found.".format( 1703 name_or_id=policy_name_or_id)) 1704 1705 kwargs['min_kbps'] = min_kbps 1706 return self.network.create_qos_minimum_bandwidth_rule(policy, **kwargs) 1707 1708 @_utils.valid_kwargs("min_kbps", "direction") 1709 def update_qos_minimum_bandwidth_rule( 1710 self, policy_name_or_id, rule_id, **kwargs 1711 ): 1712 """Update a QoS minimum bandwidth rule. 1713 1714 :param string policy_name_or_id: Name or ID of the QoS policy to which 1715 rule is associated. 1716 :param string rule_id: ID of rule to update. 1717 :param int min_kbps: Minimum bandwidth value (in kilobits per second). 1718 :param string direction: Ingress or egress. 1719 The direction in which the traffic will be available. 1720 1721 :returns: The updated QoS minimum bandwidth rule. 1722 :raises: OpenStackCloudException on operation error. 1723 """ 1724 if not self._has_neutron_extension('qos'): 1725 raise exc.OpenStackCloudUnavailableExtension( 1726 'QoS extension is not available on target cloud') 1727 1728 policy = self.network.find_qos_policy(policy_name_or_id) 1729 if not policy: 1730 raise exc.OpenStackCloudResourceNotFound( 1731 "QoS policy {name_or_id} not Found.".format( 1732 name_or_id=policy_name_or_id)) 1733 1734 if not kwargs: 1735 self.log.debug("No QoS minimum bandwidth rule data to update") 1736 return 1737 1738 curr_rule = self.network.get_qos_minimum_bandwidth_rule( 1739 rule_id, policy) 1740 if not curr_rule: 1741 raise exc.OpenStackCloudException( 1742 "QoS minimum_bandwidth_rule {rule_id} not found in policy " 1743 "{policy_id}".format(rule_id=rule_id, 1744 policy_id=policy['id'])) 1745 1746 return self.network.update_qos_minimum_bandwidth_rule( 1747 curr_rule, policy, **kwargs) 1748 1749 def delete_qos_minimum_bandwidth_rule(self, policy_name_or_id, rule_id): 1750 """Delete a QoS minimum bandwidth rule. 1751 1752 :param string policy_name_or_id: Name or ID of the QoS policy to which 1753 rule is associated. 1754 :param string rule_id: ID of rule to delete. 1755 1756 :raises: OpenStackCloudException on operation error. 1757 """ 1758 if not self._has_neutron_extension('qos'): 1759 raise exc.OpenStackCloudUnavailableExtension( 1760 'QoS extension is not available on target cloud') 1761 1762 policy = self.network.find_qos_policy(policy_name_or_id) 1763 if not policy: 1764 raise exc.OpenStackCloudResourceNotFound( 1765 "QoS policy {name_or_id} not Found.".format( 1766 name_or_id=policy_name_or_id)) 1767 1768 try: 1769 self.network.delete_qos_minimum_bandwidth_rule( 1770 rule_id, policy, ignore_missing=False) 1771 except exceptions.ResourceNotFound: 1772 self.log.debug( 1773 "QoS minimum bandwidth rule {rule_id} not found in policy " 1774 "{policy_id}. Ignoring.".format(rule_id=rule_id, 1775 policy_id=policy['id'])) 1776 return False 1777 1778 return True 1779 1780 def add_router_interface(self, router, subnet_id=None, port_id=None): 1781 """Attach a subnet to an internal router interface. 1782 1783 Either a subnet ID or port ID must be specified for the internal 1784 interface. Supplying both will result in an error. 1785 1786 :param dict router: The dict object of the router being changed 1787 :param string subnet_id: The ID of the subnet to use for the interface 1788 :param string port_id: The ID of the port to use for the interface 1789 1790 :returns: A ``munch.Munch`` with the router ID (ID), 1791 subnet ID (subnet_id), port ID (port_id) and tenant ID 1792 (tenant_id). 1793 1794 :raises: OpenStackCloudException on operation error. 1795 """ 1796 json_body = {} 1797 if subnet_id: 1798 json_body['subnet_id'] = subnet_id 1799 if port_id: 1800 json_body['port_id'] = port_id 1801 1802 return proxy._json_response( 1803 self.network.put( 1804 "/routers/{router_id}/add_router_interface".format( 1805 router_id=router['id']), 1806 json=json_body), 1807 error_message="Error attaching interface to router {0}".format( 1808 router['id'])) 1809 1810 def remove_router_interface(self, router, subnet_id=None, port_id=None): 1811 """Detach a subnet from an internal router interface. 1812 1813 At least one of subnet_id or port_id must be supplied. 1814 1815 If you specify both subnet and port ID, the subnet ID must 1816 correspond to the subnet ID of the first IP address on the port 1817 specified by the port ID. Otherwise an error occurs. 1818 1819 :param dict router: The dict object of the router being changed 1820 :param string subnet_id: The ID of the subnet to use for the interface 1821 :param string port_id: The ID of the port to use for the interface 1822 1823 :returns: None on success 1824 1825 :raises: OpenStackCloudException on operation error. 1826 """ 1827 json_body = {} 1828 if subnet_id: 1829 json_body['subnet_id'] = subnet_id 1830 if port_id: 1831 json_body['port_id'] = port_id 1832 1833 if not json_body: 1834 raise ValueError( 1835 "At least one of subnet_id or port_id must be supplied.") 1836 1837 exceptions.raise_from_response( 1838 self.network.put( 1839 "/routers/{router_id}/remove_router_interface".format( 1840 router_id=router['id']), 1841 json=json_body), 1842 error_message="Error detaching interface from router {0}".format( 1843 router['id'])) 1844 1845 def list_router_interfaces(self, router, interface_type=None): 1846 """List all interfaces for a router. 1847 1848 :param dict router: A router dict object. 1849 :param string interface_type: One of None, "internal", or "external". 1850 Controls whether all, internal interfaces or external interfaces 1851 are returned. 1852 1853 :returns: A list of port ``munch.Munch`` objects. 1854 """ 1855 # Find only router interface and gateway ports, ignore L3 HA ports etc. 1856 router_interfaces = self.search_ports(filters={ 1857 'device_id': router['id'], 1858 'device_owner': 'network:router_interface'} 1859 ) + self.search_ports(filters={ 1860 'device_id': router['id'], 1861 'device_owner': 'network:router_interface_distributed'} 1862 ) + self.search_ports(filters={ 1863 'device_id': router['id'], 1864 'device_owner': 'network:ha_router_replicated_interface'}) 1865 router_gateways = self.search_ports(filters={ 1866 'device_id': router['id'], 1867 'device_owner': 'network:router_gateway'}) 1868 ports = router_interfaces + router_gateways 1869 1870 if interface_type: 1871 if interface_type == 'internal': 1872 return router_interfaces 1873 if interface_type == 'external': 1874 return router_gateways 1875 return ports 1876 1877 def create_router(self, name=None, admin_state_up=True, 1878 ext_gateway_net_id=None, enable_snat=None, 1879 ext_fixed_ips=None, project_id=None, 1880 availability_zone_hints=None): 1881 """Create a logical router. 1882 1883 :param string name: The router name. 1884 :param bool admin_state_up: The administrative state of the router. 1885 :param string ext_gateway_net_id: Network ID for the external gateway. 1886 :param bool enable_snat: Enable Source NAT (SNAT) attribute. 1887 :param ext_fixed_ips: 1888 List of dictionaries of desired IP and/or subnet on the 1889 external network. Example:: 1890 1891 [ 1892 { 1893 "subnet_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", 1894 "ip_address": "192.168.10.2" 1895 } 1896 ] 1897 :param string project_id: Project ID for the router. 1898 :param types.ListType availability_zone_hints: 1899 A list of availability zone hints. 1900 1901 :returns: The router object. 1902 :raises: OpenStackCloudException on operation error. 1903 """ 1904 router = { 1905 'admin_state_up': admin_state_up 1906 } 1907 if project_id is not None: 1908 router['tenant_id'] = project_id 1909 if name: 1910 router['name'] = name 1911 ext_gw_info = self._build_external_gateway_info( 1912 ext_gateway_net_id, enable_snat, ext_fixed_ips 1913 ) 1914 if ext_gw_info: 1915 router['external_gateway_info'] = ext_gw_info 1916 if availability_zone_hints is not None: 1917 if not isinstance(availability_zone_hints, list): 1918 raise exc.OpenStackCloudException( 1919 "Parameter 'availability_zone_hints' must be a list") 1920 if not self._has_neutron_extension('router_availability_zone'): 1921 raise exc.OpenStackCloudUnavailableExtension( 1922 'router_availability_zone extension is not available on ' 1923 'target cloud') 1924 router['availability_zone_hints'] = availability_zone_hints 1925 1926 data = proxy._json_response( 1927 self.network.post("/routers", json={"router": router}), 1928 error_message="Error creating router {0}".format(name)) 1929 return self._get_and_munchify('router', data) 1930 1931 def update_router(self, name_or_id, name=None, admin_state_up=None, 1932 ext_gateway_net_id=None, enable_snat=None, 1933 ext_fixed_ips=None, routes=None): 1934 """Update an existing logical router. 1935 1936 :param string name_or_id: The name or UUID of the router to update. 1937 :param string name: The new router name. 1938 :param bool admin_state_up: The administrative state of the router. 1939 :param string ext_gateway_net_id: 1940 The network ID for the external gateway. 1941 :param bool enable_snat: Enable Source NAT (SNAT) attribute. 1942 :param ext_fixed_ips: 1943 List of dictionaries of desired IP and/or subnet on the 1944 external network. Example:: 1945 1946 [ 1947 { 1948 "subnet_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b", 1949 "ip_address": "192.168.10.2" 1950 } 1951 ] 1952 :param list routes: 1953 A list of dictionaries with destination and nexthop parameters. To 1954 clear all routes pass an empty list ([]). 1955 1956 Example:: 1957 1958 [ 1959 { 1960 "destination": "179.24.1.0/24", 1961 "nexthop": "172.24.3.99" 1962 } 1963 ] 1964 :returns: The router object. 1965 :raises: OpenStackCloudException on operation error. 1966 """ 1967 router = {} 1968 if name: 1969 router['name'] = name 1970 if admin_state_up is not None: 1971 router['admin_state_up'] = admin_state_up 1972 ext_gw_info = self._build_external_gateway_info( 1973 ext_gateway_net_id, enable_snat, ext_fixed_ips 1974 ) 1975 if ext_gw_info: 1976 router['external_gateway_info'] = ext_gw_info 1977 1978 if routes is not None: 1979 if self._has_neutron_extension('extraroute'): 1980 router['routes'] = routes 1981 else: 1982 self.log.warning( 1983 'extra routes extension is not available on target cloud') 1984 1985 if not router: 1986 self.log.debug("No router data to update") 1987 return 1988 1989 curr_router = self.get_router(name_or_id) 1990 if not curr_router: 1991 raise exc.OpenStackCloudException( 1992 "Router %s not found." % name_or_id) 1993 1994 resp = self.network.put( 1995 "/routers/{router_id}".format(router_id=curr_router['id']), 1996 json={"router": router}) 1997 data = proxy._json_response( 1998 resp, 1999 error_message="Error updating router {0}".format(name_or_id)) 2000 return self._get_and_munchify('router', data) 2001 2002 def delete_router(self, name_or_id): 2003 """Delete a logical router. 2004 2005 If a name, instead of a unique UUID, is supplied, it is possible 2006 that we could find more than one matching router since names are 2007 not required to be unique. An error will be raised in this case. 2008 2009 :param name_or_id: Name or ID of the router being deleted. 2010 2011 :returns: True if delete succeeded, False otherwise. 2012 2013 :raises: OpenStackCloudException on operation error. 2014 """ 2015 router = self.get_router(name_or_id) 2016 if not router: 2017 self.log.debug("Router %s not found for deleting", name_or_id) 2018 return False 2019 2020 exceptions.raise_from_response(self.network.delete( 2021 "/routers/{router_id}".format(router_id=router['id']), 2022 error_message="Error deleting router {0}".format(name_or_id))) 2023 2024 return True 2025 2026 def create_subnet(self, network_name_or_id, cidr=None, ip_version=4, 2027 enable_dhcp=False, subnet_name=None, tenant_id=None, 2028 allocation_pools=None, 2029 gateway_ip=None, disable_gateway_ip=False, 2030 dns_nameservers=None, host_routes=None, 2031 ipv6_ra_mode=None, ipv6_address_mode=None, 2032 prefixlen=None, use_default_subnetpool=False, **kwargs): 2033 """Create a subnet on a specified network. 2034 2035 :param string network_name_or_id: 2036 The unique name or ID of the attached network. If a non-unique 2037 name is supplied, an exception is raised. 2038 :param string cidr: 2039 The CIDR. 2040 :param int ip_version: 2041 The IP version, which is 4 or 6. 2042 :param bool enable_dhcp: 2043 Set to ``True`` if DHCP is enabled and ``False`` if disabled. 2044 Default is ``False``. 2045 :param string subnet_name: 2046 The name of the subnet. 2047 :param string tenant_id: 2048 The ID of the tenant who owns the network. Only administrative users 2049 can specify a tenant ID other than their own. 2050 :param allocation_pools: 2051 A list of dictionaries of the start and end addresses for the 2052 allocation pools. For example:: 2053 2054 [ 2055 { 2056 "start": "192.168.199.2", 2057 "end": "192.168.199.254" 2058 } 2059 ] 2060 2061 :param string gateway_ip: 2062 The gateway IP address. When you specify both allocation_pools and 2063 gateway_ip, you must ensure that the gateway IP does not overlap 2064 with the specified allocation pools. 2065 :param bool disable_gateway_ip: 2066 Set to ``True`` if gateway IP address is disabled and ``False`` if 2067 enabled. It is not allowed with gateway_ip. 2068 Default is ``False``. 2069 :param dns_nameservers: 2070 A list of DNS name servers for the subnet. For example:: 2071 2072 [ "8.8.8.7", "8.8.8.8" ] 2073 2074 :param host_routes: 2075 A list of host route dictionaries for the subnet. For example:: 2076 2077 [ 2078 { 2079 "destination": "0.0.0.0/0", 2080 "nexthop": "123.456.78.9" 2081 }, 2082 { 2083 "destination": "192.168.0.0/24", 2084 "nexthop": "192.168.0.1" 2085 } 2086 ] 2087 2088 :param string ipv6_ra_mode: 2089 IPv6 Router Advertisement mode. Valid values are: 'dhcpv6-stateful', 2090 'dhcpv6-stateless', or 'slaac'. 2091 :param string ipv6_address_mode: 2092 IPv6 address mode. Valid values are: 'dhcpv6-stateful', 2093 'dhcpv6-stateless', or 'slaac'. 2094 :param string prefixlen: 2095 The prefix length to use for subnet allocation from a subnet pool. 2096 :param bool use_default_subnetpool: 2097 Use the default subnetpool for ``ip_version`` to obtain a CIDR. It 2098 is required to pass ``None`` to the ``cidr`` argument when enabling 2099 this option. 2100 :param kwargs: Key value pairs to be passed to the Neutron API. 2101 2102 :returns: The new subnet object. 2103 :raises: OpenStackCloudException on operation error. 2104 """ 2105 2106 if tenant_id is not None: 2107 filters = {'tenant_id': tenant_id} 2108 else: 2109 filters = None 2110 2111 network = self.get_network(network_name_or_id, filters) 2112 if not network: 2113 raise exc.OpenStackCloudException( 2114 "Network %s not found." % network_name_or_id) 2115 2116 if disable_gateway_ip and gateway_ip: 2117 raise exc.OpenStackCloudException( 2118 'arg:disable_gateway_ip is not allowed with arg:gateway_ip') 2119 2120 if not cidr and not use_default_subnetpool: 2121 raise exc.OpenStackCloudException( 2122 'arg:cidr is required when a subnetpool is not used') 2123 2124 if cidr and use_default_subnetpool: 2125 raise exc.OpenStackCloudException( 2126 'arg:cidr must be set to None when use_default_subnetpool == ' 2127 'True') 2128 2129 # Be friendly on ip_version and allow strings 2130 if isinstance(ip_version, str): 2131 try: 2132 ip_version = int(ip_version) 2133 except ValueError: 2134 raise exc.OpenStackCloudException( 2135 'ip_version must be an integer') 2136 2137 # The body of the neutron message for the subnet we wish to create. 2138 # This includes attributes that are required or have defaults. 2139 subnet = dict({ 2140 'network_id': network['id'], 2141 'ip_version': ip_version, 2142 'enable_dhcp': enable_dhcp, 2143 }, **kwargs) 2144 2145 # Add optional attributes to the message. 2146 if cidr: 2147 subnet['cidr'] = cidr 2148 if subnet_name: 2149 subnet['name'] = subnet_name 2150 if tenant_id: 2151 subnet['tenant_id'] = tenant_id 2152 if allocation_pools: 2153 subnet['allocation_pools'] = allocation_pools 2154 if gateway_ip: 2155 subnet['gateway_ip'] = gateway_ip 2156 if disable_gateway_ip: 2157 subnet['gateway_ip'] = None 2158 if dns_nameservers: 2159 subnet['dns_nameservers'] = dns_nameservers 2160 if host_routes: 2161 subnet['host_routes'] = host_routes 2162 if ipv6_ra_mode: 2163 subnet['ipv6_ra_mode'] = ipv6_ra_mode 2164 if ipv6_address_mode: 2165 subnet['ipv6_address_mode'] = ipv6_address_mode 2166 if prefixlen: 2167 subnet['prefixlen'] = prefixlen 2168 if use_default_subnetpool: 2169 subnet['use_default_subnetpool'] = True 2170 2171 response = self.network.post("/subnets", json={"subnet": subnet}) 2172 2173 return self._get_and_munchify('subnet', response) 2174 2175 def delete_subnet(self, name_or_id): 2176 """Delete a subnet. 2177 2178 If a name, instead of a unique UUID, is supplied, it is possible 2179 that we could find more than one matching subnet since names are 2180 not required to be unique. An error will be raised in this case. 2181 2182 :param name_or_id: Name or ID of the subnet being deleted. 2183 2184 :returns: True if delete succeeded, False otherwise. 2185 2186 :raises: OpenStackCloudException on operation error. 2187 """ 2188 subnet = self.get_subnet(name_or_id) 2189 if not subnet: 2190 self.log.debug("Subnet %s not found for deleting", name_or_id) 2191 return False 2192 2193 exceptions.raise_from_response(self.network.delete( 2194 "/subnets/{subnet_id}".format(subnet_id=subnet['id']))) 2195 return True 2196 2197 def update_subnet(self, name_or_id, subnet_name=None, enable_dhcp=None, 2198 gateway_ip=None, disable_gateway_ip=None, 2199 allocation_pools=None, dns_nameservers=None, 2200 host_routes=None): 2201 """Update an existing subnet. 2202 2203 :param string name_or_id: 2204 Name or ID of the subnet to update. 2205 :param string subnet_name: 2206 The new name of the subnet. 2207 :param bool enable_dhcp: 2208 Set to ``True`` if DHCP is enabled and ``False`` if disabled. 2209 :param string gateway_ip: 2210 The gateway IP address. When you specify both allocation_pools and 2211 gateway_ip, you must ensure that the gateway IP does not overlap 2212 with the specified allocation pools. 2213 :param bool disable_gateway_ip: 2214 Set to ``True`` if gateway IP address is disabled and ``False`` if 2215 enabled. It is not allowed with gateway_ip. 2216 Default is ``False``. 2217 :param allocation_pools: 2218 A list of dictionaries of the start and end addresses for the 2219 allocation pools. For example:: 2220 2221 [ 2222 { 2223 "start": "192.168.199.2", 2224 "end": "192.168.199.254" 2225 } 2226 ] 2227 2228 :param dns_nameservers: 2229 A list of DNS name servers for the subnet. For example:: 2230 2231 [ "8.8.8.7", "8.8.8.8" ] 2232 2233 :param host_routes: 2234 A list of host route dictionaries for the subnet. For example:: 2235 2236 [ 2237 { 2238 "destination": "0.0.0.0/0", 2239 "nexthop": "123.456.78.9" 2240 }, 2241 { 2242 "destination": "192.168.0.0/24", 2243 "nexthop": "192.168.0.1" 2244 } 2245 ] 2246 2247 :returns: The updated subnet object. 2248 :raises: OpenStackCloudException on operation error. 2249 """ 2250 subnet = {} 2251 if subnet_name: 2252 subnet['name'] = subnet_name 2253 if enable_dhcp is not None: 2254 subnet['enable_dhcp'] = enable_dhcp 2255 if gateway_ip: 2256 subnet['gateway_ip'] = gateway_ip 2257 if disable_gateway_ip: 2258 subnet['gateway_ip'] = None 2259 if allocation_pools: 2260 subnet['allocation_pools'] = allocation_pools 2261 if dns_nameservers: 2262 subnet['dns_nameservers'] = dns_nameservers 2263 if host_routes: 2264 subnet['host_routes'] = host_routes 2265 2266 if not subnet: 2267 self.log.debug("No subnet data to update") 2268 return 2269 2270 if disable_gateway_ip and gateway_ip: 2271 raise exc.OpenStackCloudException( 2272 'arg:disable_gateway_ip is not allowed with arg:gateway_ip') 2273 2274 curr_subnet = self.get_subnet(name_or_id) 2275 if not curr_subnet: 2276 raise exc.OpenStackCloudException( 2277 "Subnet %s not found." % name_or_id) 2278 2279 response = self.network.put( 2280 "/subnets/{subnet_id}".format(subnet_id=curr_subnet['id']), 2281 json={"subnet": subnet}) 2282 return self._get_and_munchify('subnet', response) 2283 2284 @_utils.valid_kwargs('name', 'admin_state_up', 'mac_address', 'fixed_ips', 2285 'subnet_id', 'ip_address', 'security_groups', 2286 'allowed_address_pairs', 'extra_dhcp_opts', 2287 'device_owner', 'device_id', 'binding:vnic_type', 2288 'binding:profile', 'port_security_enabled', 2289 'qos_policy_id', 'binding:host_id') 2290 def create_port(self, network_id, **kwargs): 2291 """Create a port 2292 2293 :param network_id: The ID of the network. (Required) 2294 :param name: A symbolic name for the port. (Optional) 2295 :param admin_state_up: The administrative status of the port, 2296 which is up (true, default) or down (false). (Optional) 2297 :param mac_address: The MAC address. (Optional) 2298 :param fixed_ips: List of ip_addresses and subnet_ids. See subnet_id 2299 and ip_address. (Optional) 2300 For example:: 2301 2302 [ 2303 { 2304 "ip_address": "10.29.29.13", 2305 "subnet_id": "a78484c4-c380-4b47-85aa-21c51a2d8cbd" 2306 }, ... 2307 ] 2308 :param subnet_id: If you specify only a subnet ID, OpenStack Networking 2309 allocates an available IP from that subnet to the port. (Optional) 2310 If you specify both a subnet ID and an IP address, OpenStack 2311 Networking tries to allocate the specified address to the port. 2312 :param ip_address: If you specify both a subnet ID and an IP address, 2313 OpenStack Networking tries to allocate the specified address to 2314 the port. 2315 :param security_groups: List of security group UUIDs. (Optional) 2316 :param allowed_address_pairs: Allowed address pairs list (Optional) 2317 For example:: 2318 2319 [ 2320 { 2321 "ip_address": "23.23.23.1", 2322 "mac_address": "fa:16:3e:c4:cd:3f" 2323 }, ... 2324 ] 2325 :param extra_dhcp_opts: Extra DHCP options. (Optional). 2326 For example:: 2327 2328 [ 2329 { 2330 "opt_name": "opt name1", 2331 "opt_value": "value1" 2332 }, ... 2333 ] 2334 :param device_owner: The ID of the entity that uses this port. 2335 For example, a DHCP agent. (Optional) 2336 :param device_id: The ID of the device that uses this port. 2337 For example, a virtual server. (Optional) 2338 :param binding vnic_type: The type of the created port. (Optional) 2339 :param port_security_enabled: The security port state created on 2340 the network. (Optional) 2341 :param qos_policy_id: The ID of the QoS policy to apply for port. 2342 2343 :returns: a ``munch.Munch`` describing the created port. 2344 2345 :raises: ``OpenStackCloudException`` on operation error. 2346 """ 2347 kwargs['network_id'] = network_id 2348 2349 data = proxy._json_response( 2350 self.network.post("/ports", json={'port': kwargs}), 2351 error_message="Error creating port for network {0}".format( 2352 network_id)) 2353 return self._get_and_munchify('port', data) 2354 2355 @_utils.valid_kwargs('name', 'admin_state_up', 'fixed_ips', 2356 'security_groups', 'allowed_address_pairs', 2357 'extra_dhcp_opts', 'device_owner', 'device_id', 2358 'binding:vnic_type', 'binding:profile', 2359 'port_security_enabled', 'qos_policy_id', 2360 'binding:host_id') 2361 def update_port(self, name_or_id, **kwargs): 2362 """Update a port 2363 2364 Note: to unset an attribute use None value. To leave an attribute 2365 untouched just omit it. 2366 2367 :param name_or_id: name or ID of the port to update. (Required) 2368 :param name: A symbolic name for the port. (Optional) 2369 :param admin_state_up: The administrative status of the port, 2370 which is up (true) or down (false). (Optional) 2371 :param fixed_ips: List of ip_addresses and subnet_ids. (Optional) 2372 If you specify only a subnet ID, OpenStack Networking allocates 2373 an available IP from that subnet to the port. 2374 If you specify both a subnet ID and an IP address, OpenStack 2375 Networking tries to allocate the specified address to the port. 2376 For example:: 2377 2378 [ 2379 { 2380 "ip_address": "10.29.29.13", 2381 "subnet_id": "a78484c4-c380-4b47-85aa-21c51a2d8cbd" 2382 }, ... 2383 ] 2384 :param security_groups: List of security group UUIDs. (Optional) 2385 :param allowed_address_pairs: Allowed address pairs list (Optional) 2386 For example:: 2387 2388 [ 2389 { 2390 "ip_address": "23.23.23.1", 2391 "mac_address": "fa:16:3e:c4:cd:3f" 2392 }, ... 2393 ] 2394 :param extra_dhcp_opts: Extra DHCP options. (Optional). 2395 For example:: 2396 2397 [ 2398 { 2399 "opt_name": "opt name1", 2400 "opt_value": "value1" 2401 }, ... 2402 ] 2403 :param device_owner: The ID of the entity that uses this port. 2404 For example, a DHCP agent. (Optional) 2405 :param device_id: The ID of the resource this port is attached to. 2406 :param binding vnic_type: The type of the created port. (Optional) 2407 :param port_security_enabled: The security port state created on 2408 the network. (Optional) 2409 :param qos_policy_id: The ID of the QoS policy to apply for port. 2410 2411 :returns: a ``munch.Munch`` describing the updated port. 2412 2413 :raises: OpenStackCloudException on operation error. 2414 """ 2415 port = self.get_port(name_or_id=name_or_id) 2416 if port is None: 2417 raise exc.OpenStackCloudException( 2418 "failed to find port '{port}'".format(port=name_or_id)) 2419 2420 data = proxy._json_response( 2421 self.network.put( 2422 "/ports/{port_id}".format(port_id=port['id']), 2423 json={"port": kwargs}), 2424 error_message="Error updating port {0}".format(name_or_id)) 2425 return self._get_and_munchify('port', data) 2426 2427 def delete_port(self, name_or_id): 2428 """Delete a port 2429 2430 :param name_or_id: ID or name of the port to delete. 2431 2432 :returns: True if delete succeeded, False otherwise. 2433 2434 :raises: OpenStackCloudException on operation error. 2435 """ 2436 port = self.get_port(name_or_id=name_or_id) 2437 if port is None: 2438 self.log.debug("Port %s not found for deleting", name_or_id) 2439 return False 2440 2441 exceptions.raise_from_response( 2442 self.network.delete( 2443 "/ports/{port_id}".format(port_id=port['id'])), 2444 error_message="Error deleting port {0}".format(name_or_id)) 2445 return True 2446 2447 def _get_port_ids(self, name_or_id_list, filters=None): 2448 """ 2449 Takes a list of port names or ids, retrieves ports and returns a list 2450 with port ids only. 2451 2452 :param list[str] name_or_id_list: list of port names or ids 2453 :param dict filters: optional filters 2454 :raises: SDKException on multiple matches 2455 :raises: ResourceNotFound if a port is not found 2456 :return: list of port ids 2457 :rtype: list[str] 2458 """ 2459 ids_list = [] 2460 for name_or_id in name_or_id_list: 2461 port = self.get_port(name_or_id, filters) 2462 if not port: 2463 raise exceptions.ResourceNotFound( 2464 'Port {id} not found'.format(id=name_or_id)) 2465 ids_list.append(port['id']) 2466 return ids_list 2467 2468 def _build_external_gateway_info(self, ext_gateway_net_id, enable_snat, 2469 ext_fixed_ips): 2470 info = {} 2471 if ext_gateway_net_id: 2472 info['network_id'] = ext_gateway_net_id 2473 # Only send enable_snat if it is explicitly set. 2474 if enable_snat is not None: 2475 info['enable_snat'] = enable_snat 2476 if ext_fixed_ips: 2477 info['external_fixed_ips'] = ext_fixed_ips 2478 if info: 2479 return info 2480 return None 2481