9372716befa6d8e70fd11bd33e8f69e80a046a51
[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     pass
49
50
51 class ChainRouter(object):
52     """Could be a shared router across all chains or a chain private router."""
53
54     def __init__(self, manager, name, subnets, routes):
55         """Create a router for given chain."""
56         self.manager = manager
57         self.subnets = subnets
58         self.routes = routes
59         self.name = name
60         self.ports = [None, None]
61         self.reuse = False
62         self.router = None
63         try:
64             self._setup()
65         except Exception:
66             LOG.error("Error creating router %s", self.name)
67             self.delete()
68             raise
69
70     def _setup(self):
71         # Lookup if there is a matching router with same name
72         routers = self.manager.neutron_client.list_routers(name=self.name)
73
74         if routers['routers']:
75             router = routers['routers'][0]
76             # a router of same name already exists, we need to verify it has the same
77             # characteristics
78             if self.subnets:
79                 for subnet in self.subnets:
80                     if not self.get_router_interface(router['id'], subnet.network['subnets'][0]):
81                         raise ChainException("Mismatch of 'subnet_id' for reused "
82                                              "router '{router}'.Router has no subnet id '{sub_id}'."
83                                              .format(router=self.name,
84                                                      sub_id=subnet.network['subnets'][0]))
85                 interfaces = self.manager.neutron_client.list_ports(device_id=router['id'])['ports']
86                 for interface in interfaces:
87                     if self.is_ip_in_network(
88                             interface['fixed_ips'][0]['ip_address'],
89                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]) \
90                         or self.is_ip_in_network(
91                             interface['fixed_ips'][0]['ip_address'],
92                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]):
93                         self.ports[0] = interface
94                     else:
95                         self.ports[1] = interface
96             if self.routes:
97                 for route in self.routes:
98                     if route not in router['routes']:
99                         LOG.info("Mismatch of 'router' for reused router '%s'."
100                                  "Router has no existing route destination '%s', "
101                                  "and nexthop '%s'.", self.name,
102                                  route['destination'],
103                                  route['nexthop'])
104                         LOG.info("New route added to router %s for reused ", self.name)
105                         body = {
106                             'router': {
107                                 'routes': self.routes
108                             }
109                         }
110                         self.manager.neutron_client.update_router(router['id'], body)
111
112             LOG.info('Reusing existing router: %s', self.name)
113             self.reuse = True
114             self.router = router
115             return
116
117         body = {
118             'router': {
119                 'name': self.name,
120                 'admin_state_up': True
121             }
122         }
123         router = self.manager.neutron_client.create_router(body)['router']
124         router_id = router['id']
125
126         if self.subnets:
127             for subnet in self.subnets:
128                 router_interface = {'subnet_id': subnet.network['subnets'][0]}
129                 self.manager.neutron_client.add_interface_router(router_id, router_interface)
130             interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
131             for interface in interfaces:
132                 itf = interface['fixed_ips'][0]['ip_address']
133                 cidr0 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]
134                 cidr1 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]
135                 if self.is_ip_in_network(itf, cidr0) or self.is_ip_in_network(itf, cidr1):
136                     self.ports[0] = interface
137                 else:
138                     self.ports[1] = interface
139
140         if self.routes:
141             body = {
142                 'router': {
143                     'routes': self.routes
144                 }
145             }
146             self.manager.neutron_client.update_router(router_id, body)
147
148         LOG.info('Created router: %s.', self.name)
149         self.router = self.manager.neutron_client.show_router(router_id)
150
151     def get_uuid(self):
152         """
153         Extract UUID of this router.
154
155         :return: UUID of this router
156         """
157         return self.router['id']
158
159     def get_router_interface(self, router_id, subnet_id):
160         interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
161         matching_interface = None
162         for interface in interfaces:
163             if interface['fixed_ips'][0]['subnet_id'] == subnet_id:
164                 matching_interface = interface
165         return matching_interface
166
167     def is_ip_in_network(self, interface_ip, cidr):
168         return IPAddress(interface_ip) in IPNetwork(cidr)
169
170     def delete(self):
171         """Delete this router."""
172         if not self.reuse and self.router:
173             retry = 0
174             while retry < self.manager.config.generic_retry_count:
175                 try:
176                     self.manager.neutron_client.delete_router(self.router['id'])
177                     LOG.info("Deleted router: %s", self.name)
178                     return
179                 except Exception:
180                     retry += 1
181                     LOG.info('Error deleting router %s (retry %d/%d)...',
182                              self.name,
183                              retry,
184                              self.manager.config.generic_retry_count)
185                     time.sleep(self.manager.config.generic_poll_sec)
186             LOG.error('Unable to delete router: %s', self.name)