NFVBENCH-153 Add support for python3
[nfvbench.git] / nfvbench / chain_router.py
1 #!/usr/bin/env python
2 # Copyright 2018 Cisco Systems, Inc.  All rights reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 #
16
17 # This module takes care of chaining routers
18 #
19 """NFVBENCH CHAIN DISCOVERY/STAGING.
20
21 This module takes care of staging/discovering resources that are participating in a
22 L3 benchmarking session: routers, networks, ports, routes.
23 If a resource is discovered with the same name, it will be reused.
24 Otherwise it will be created.
25
26 Once created/discovered, instances are checked to be in the active state (ready to pass traffic)
27 Configuration parameters that will influence how these resources are staged/related:
28 - openstack or no openstack
29 - chain type
30 - number of chains
31 - number of VNF in each chain (PVP, PVVP)
32 - SRIOV and middle port SRIOV for port types
33 - whether networks are shared across chains or not
34
35 There is not traffic generation involved in this module.
36 """
37 import time
38
39 from netaddr import IPAddress
40 from netaddr import IPNetwork
41
42 from .log import LOG
43
44
45 class ChainException(Exception):
46     """Exception while operating the chains."""
47
48 class ChainRouter(object):
49     """Could be a shared router across all chains or a chain private router."""
50
51     def __init__(self, manager, name, subnets, routes):
52         """Create a router for given chain."""
53         self.manager = manager
54         self.subnets = subnets
55         self.routes = routes
56         self.name = name
57         self.ports = [None, None]
58         self.reuse = False
59         self.router = None
60         try:
61             self._setup()
62         except Exception:
63             LOG.error("Error creating router %s", self.name)
64             self.delete()
65             raise
66
67     def _setup(self):
68         # Lookup if there is a matching router with same name
69         routers = self.manager.neutron_client.list_routers(name=self.name)
70
71         if routers['routers']:
72             router = routers['routers'][0]
73             # a router of same name already exists, we need to verify it has the same
74             # characteristics
75             if self.subnets:
76                 for subnet in self.subnets:
77                     if not self.get_router_interface(router['id'], subnet.network['subnets'][0]):
78                         raise ChainException("Mismatch of 'subnet_id' for reused "
79                                              "router '{router}'.Router has no subnet id '{sub_id}'."
80                                              .format(router=self.name,
81                                                      sub_id=subnet.network['subnets'][0]))
82                 interfaces = self.manager.neutron_client.list_ports(device_id=router['id'])['ports']
83                 for interface in interfaces:
84                     if self.is_ip_in_network(
85                             interface['fixed_ips'][0]['ip_address'],
86                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]) \
87                         or self.is_ip_in_network(
88                             interface['fixed_ips'][0]['ip_address'],
89                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]):
90                         self.ports[0] = interface
91                     else:
92                         self.ports[1] = interface
93             if self.routes:
94                 for route in self.routes:
95                     if route not in router['routes']:
96                         LOG.info("Mismatch of 'router' for reused router '%s'."
97                                  "Router has no existing route destination '%s', "
98                                  "and nexthop '%s'.", self.name,
99                                  route['destination'],
100                                  route['nexthop'])
101                         LOG.info("New route added to router %s for reused ", self.name)
102                         body = {
103                             'router': {
104                                 'routes': self.routes
105                             }
106                         }
107                         self.manager.neutron_client.update_router(router['id'], body)
108
109             LOG.info('Reusing existing router: %s', self.name)
110             self.reuse = True
111             self.router = router
112             return
113
114         body = {
115             'router': {
116                 'name': self.name,
117                 'admin_state_up': True
118             }
119         }
120         router = self.manager.neutron_client.create_router(body)['router']
121         router_id = router['id']
122
123         if self.subnets:
124             for subnet in self.subnets:
125                 router_interface = {'subnet_id': subnet.network['subnets'][0]}
126                 self.manager.neutron_client.add_interface_router(router_id, router_interface)
127             interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
128             for interface in interfaces:
129                 itf = interface['fixed_ips'][0]['ip_address']
130                 cidr0 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]
131                 cidr1 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]
132                 if self.is_ip_in_network(itf, cidr0) or self.is_ip_in_network(itf, cidr1):
133                     self.ports[0] = interface
134                 else:
135                     self.ports[1] = interface
136
137         if self.routes:
138             body = {
139                 'router': {
140                     'routes': self.routes
141                 }
142             }
143             self.manager.neutron_client.update_router(router_id, body)
144
145         LOG.info('Created router: %s.', self.name)
146         self.router = self.manager.neutron_client.show_router(router_id)
147
148     def get_uuid(self):
149         """
150         Extract UUID of this router.
151
152         :return: UUID of this router
153         """
154         return self.router['id']
155
156     def get_router_interface(self, router_id, subnet_id):
157         interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
158         matching_interface = None
159         for interface in interfaces:
160             if interface['fixed_ips'][0]['subnet_id'] == subnet_id:
161                 matching_interface = interface
162         return matching_interface
163
164     def is_ip_in_network(self, interface_ip, cidr):
165         return IPAddress(interface_ip) in IPNetwork(cidr)
166
167     def delete(self):
168         """Delete this router."""
169         if not self.reuse and self.router:
170             retry = 0
171             while retry < self.manager.config.generic_retry_count:
172                 try:
173                     self.manager.neutron_client.delete_router(self.router['id'])
174                     LOG.info("Deleted router: %s", self.name)
175                     return
176                 except Exception:
177                     retry += 1
178                     LOG.info('Error deleting router %s (retry %d/%d)...',
179                              self.name,
180                              retry,
181                              self.manager.config.generic_retry_count)
182                     time.sleep(self.manager.config.generic_poll_sec)
183             LOG.error('Unable to delete router: %s', self.name)