l3-router fixes
[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                 # This string filters nfvbench networks in case when some other specific networks
84                 # created and attached to the test nfvebnch router manually or automatically
85                 # like in case of HA when neutron router virtually present on several network nodes
86                 interfaces = [x for x in interfaces if x['fixed_ips'][0]['subnet_id'] in
87                               [s.network['subnets'][0] for s in self.subnets]]
88                 for interface in interfaces:
89                     if self.is_ip_in_network(
90                             interface['fixed_ips'][0]['ip_address'],
91                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]) \
92                         or self.is_ip_in_network(
93                             interface['fixed_ips'][0]['ip_address'],
94                             self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]):
95                         self.ports[0] = interface
96                     else:
97                         self.ports[1] = interface
98             if self.routes:
99                 for route in self.routes:
100                     if route not in router['routes']:
101                         LOG.info("Mismatch of 'router' for reused router '%s'."
102                                  "Router has no existing route destination '%s', "
103                                  "and nexthop '%s'.", self.name,
104                                  route['destination'],
105                                  route['nexthop'])
106                         LOG.info("New route added to router %s for reused ", self.name)
107                         body = {
108                             'router': {
109                                 'routes': self.routes
110                             }
111                         }
112                         self.manager.neutron_client.update_router(router['id'], body)
113
114             LOG.info('Reusing existing router: %s', self.name)
115             self.reuse = True
116             self.router = router
117             return
118
119         body = {
120             'router': {
121                 'name': self.name,
122                 'admin_state_up': True
123             }
124         }
125         router = self.manager.neutron_client.create_router(body)['router']
126         router_id = router['id']
127
128         if self.subnets:
129             for subnet in self.subnets:
130                 router_interface = {'subnet_id': subnet.network['subnets'][0]}
131                 self.manager.neutron_client.add_interface_router(router_id, router_interface)
132             interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
133             interfaces = [x for x in interfaces if x['fixed_ips'][0]['subnet_id'] in
134                           [s.network['subnets'][0] for s in self.subnets]]
135             for interface in interfaces:
136                 itf = interface['fixed_ips'][0]['ip_address']
137                 cidr0 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[0]
138                 cidr1 = self.manager.config.traffic_generator.tg_gateway_ip_cidrs[1]
139                 if self.is_ip_in_network(itf, cidr0) or self.is_ip_in_network(itf, cidr1):
140                     self.ports[0] = interface
141                 else:
142                     self.ports[1] = interface
143
144         if self.routes:
145             body = {
146                 'router': {
147                     'routes': self.routes
148                 }
149             }
150             self.manager.neutron_client.update_router(router_id, body)
151
152         LOG.info('Created router: %s.', self.name)
153         self.router = self.manager.neutron_client.show_router(router_id)
154
155     def get_uuid(self):
156         """
157         Extract UUID of this router.
158
159         :return: UUID of this router
160         """
161         return self.router['id']
162
163     def get_router_interface(self, router_id, subnet_id):
164         interfaces = self.manager.neutron_client.list_ports(device_id=router_id)['ports']
165         matching_interface = None
166         for interface in interfaces:
167             if interface['fixed_ips'][0]['subnet_id'] == subnet_id:
168                 matching_interface = interface
169         return matching_interface
170
171     def is_ip_in_network(self, interface_ip, cidr):
172         return IPAddress(interface_ip) in IPNetwork(cidr)
173
174     def delete(self):
175         """Delete this router."""
176         if not self.reuse and self.router:
177             retry = 0
178             while retry < self.manager.config.generic_retry_count:
179                 try:
180                     self.manager.neutron_client.delete_router(self.router['id'])
181                     LOG.info("Deleted router: %s", self.name)
182                     return
183                 except Exception:
184                     retry += 1
185                     LOG.info('Error deleting router %s (retry %d/%d)...',
186                              self.name,
187                              retry,
188                              self.manager.config.generic_retry_count)
189                     time.sleep(self.manager.config.generic_poll_sec)
190             LOG.error('Unable to delete router: %s', self.name)