def network_info(self, **kwargs): # Access resource catalogue catalog = ndb() # Get network topology (topo_success, topo_msg) = self.ovs_ctl.get_topology() if not topo_success: # Send error message msg = '[ERROR]: Could not retrieve the network topology from ovs controller' print('failed', (time() - st) * 1000, 'ms') # Inform the user about the creation return False, msg topology = topo_msg.get('topology') catalog.set_topology(topology) # Return information return True, { "tn": { "topology": catalog.get_topology(), "capacity": catalog.get_capacity(), "routes": catalog.get_routes(), # "networks": catalog.get_networks(), "usage": catalog.get_usage(), "flows": catalog.get_flows(), "virtual_ifaces": catalog.get_virtual_ifaces() } }
def get_throughput_comply_path(self, paths, throughput): catalog = ndb() flows = catalog.get_flows() usage = catalog.get_usage() capacity = catalog.get_capacity() count = {} comply_paths = [] for path in paths: is_comply = True path_string = ''.join(map(str, path)) for p in range(0, len(path) - 1): if path_string not in count: count[path_string] = 0 count[path_string] = count[path_string] + flows[path[p]][path[p + 1]] if usage[path[p]][path[p + 1]] + throughput >= capacity[path[p]][path[p + 1]]: is_comply = False break if is_comply: comply_paths.append(path) path_to_apply = None min_count = 999999999 if len(comply_paths) > 0: for path in comply_paths: path_string = ''.join(map(str, path)) if count[path_string] < min_count: min_count = count[path_string] path_to_apply = path elif count[path_string] == min_count and len(path) < len(path_to_apply): min_count = count[path_string] path_to_apply = path return path_to_apply
def configure_flow_rules(self, src_agent, dst_agent): engine = PathEngine() catalog = ndb() topology = self.get_topology() paths = engine.get_paths(topology, src_agent.get('switch'), dst_agent.get('switch')) src = [] for path in paths: self.src_seq = self.src_seq + 1 src_host = str(self.src_seq) src.append(src_host) switches = engine.generate_match_switches(topology, path, src_agent.get('port'), dst_agent.get('port')) route = { 'ipv4_src': src_host, 'ipv4_src_netmask': '255.255.255.255', 'ipv4_dst': dst_agent.get('host'), 'ipv4_dst_netmask': '255.255.255.255', 'min_rate': None, 'max_rate': None, 'priority': 10, 'switches': switches } s_id = str(uuid4()) success, msg = self.orch.ovs_ctl.create_slice(**{ 's_id': s_id, 'route': route }) if not success: return None addresses = src_host + '-' + dst_agent.get('host') path_string = '-'.join(map(str, path)) catalog.add_virtual_iface(addresses, path_string) return src
def get_path(self, topology, src, dst, requirements): catalog = ndb() catalog.init_arrays() paths = self.get_paths(topology, src, dst) print('paths', paths) path = self.get_capable_path(paths, requirements) return path
def delete_slice(self, **kwargs): # Extract parameters from keyword arguments s_id = kwargs.get('s_id', None) route = kwargs.get('route', None) complete_remove = False catalog = ndb() if route is None: # Retrieve the route previously applied complete_remove = True route = catalog.get_route(s_id) if route is None: return False, 'Route not found for s_id ' + s_id # Send message to remove slice success, msg = self.ovs_ctl.delete_slice(**{ 's_id': s_id, 'route': route }) if success: path = route['path'] for p in range(0, len(path) - 1): catalog.add_flow_count(path[p], path[p + 1], -1) if route['throughput'] is not None: catalog.add_link_usage(path[p], path[p + 1], -route['throughput']) if complete_remove: catalog.remove_route(s_id) # Inform the user about the removal return success, msg
def analyze_paths(self, metric): catalog = ndb() routes = catalog.get_routes() path_string = catalog.get_virtual_iface(metric.get('src') + '-' + metric.get('dst')) current_latency = float(metric.get('params').get('max')) affected_slices = [ i for i in routes if routes[i].get('path_string') == path_string and routes[i].get('latency') is not None and (routes[i].get('latency') <= current_latency or current_latency < 0) ] for s_id in affected_slices: print('\t', 'SHE - starting reconfiguration of slice ', s_id) if s_id not in self.lock: try: self.lock.append(s_id) success, msg = self.orch.reconfigure_slice(**{'s_id': s_id}) if success: print('\t', 'SHE - reconfiguration finished for ', s_id) else: print('\t', 'SHE - ERROR ', s_id) self.lock.remove(s_id) except Exception as e: self.lock.remove(s_id) print('\t', 'SHE - ERROR reconfiguring ', s_id, e) #traceback.print_stack()
def find_border_switch(self, address): catalog = ndb() resp = None networks = catalog.get_networks() for network in networks: if IPAddress(address) in IPNetwork(network): resp = networks[network] break return resp
def get_latency_comply_paths(self, paths, latency): catalog = ndb() comply_paths = [] for path in paths: path_string = '-'.join(map(str, path)) metric = catalog.get_path_latency(path_string) if metric is not None and metric.get('max') != -1 and float(metric.get('max')) < float(latency): comply_paths.append(path) return comply_paths
def get_topology(self): (topo_success, topo_msg) = self.orch.ovs_ctl.get_topology() if not topo_success: return None topology = topo_msg.get('topology') catalog = ndb() catalog.set_topology(topology) return topology
def reconfigure_slice(self, **kwargs): s_id = kwargs.get('s_id', None) catalog = ndb() old_route = catalog.get_route(s_id) source = old_route.get('src') destination = old_route.get('dst') latency = old_route.get('latency') throughput = old_route.get('throughput') slice_args = { 's_id': s_id, 'source': source, 'destination': destination, 'requirements': { 'throughput': throughput, 'latency': latency } } print('slice args ', slice_args) (success, msg) = self.create_slice(**slice_args) print('create success ', success) if success: switches = [] new_route = catalog.get_route(s_id) print('new_route', new_route) if old_route.get('path_string') != new_route.get('path_string'): for old in old_route.get('switches'): current = self.get_in_switches(old, new_route.get('switches')) if len(current) > 0: #if old.get('in_port') != current[0].get('in_port'): # switches.append(old) if old.get('in_port') != current[0].get('in_port'): if old.get('out_port') != current[0].get( 'out_port'): old['direction'] = 'full' switches.append(old) else: old['direction'] = 'half-fw' switches.append(old) elif old.get('out_port') != current[0].get('out_port'): old['direction'] = 'half-rv' switches.append(old) else: old['direction'] = 'full' switches.append(old) route_to_delete = self.generate_route_to_delete( old_route, switches) success, msg = self.delete_slice(**{ 's_id': s_id, 'route': route_to_delete }) return success, msg
def request_slice(self, **kwargs): s_id = kwargs.get('s_id', None) catalog = ndb() if s_id is not None: route = catalog.get_route(s_id) if route is not None: msg = {} msg[s_id] = route else: msg = catalog.get_routes() return (False, "Service not found.") \ if (s_id and not msg) else (True, msg)
def __init__(self, orch, host, port): Thread.__init__(self) self.orch = orch self.src_seq = ipaddress.ip_address("10.10.0.0") self.shutdown_flag = Event() self._server_bind(host, port) catalog = ndb() #agent = catalog.add_local_agent('sonar-req02', '10.0.0.30', 'ith0', 's01', 5) #agent = catalog.add_local_agent('sonar-p01', '10.1.0.1', 'ith0', 's05', 3) agent = catalog.add_local_agent('sonar-local-agent01', '100.1.3.3', 'sth01', 's01', 1) agent = catalog.add_local_agent('sonar-local-agent02', '100.1.3.4', 'sth01', 's05', 4)
def report(self, request): t_id = request.get('t_id') catalog = ndb() broker = nem() for metric in request.get('metrics'): if metric.get('type') == 'latency': path_string = catalog.get_virtual_iface( metric.get('src') + '-' + metric.get('dst')) catalog.set_path_latency(path_string, metric.get('params')) broker.insert_metric(metric) #self.check_paths(path_string, metric.get('params')) resp = {"id": t_id, "type": "report_resp", "result_code": 0} return resp
def create_slice(self, **kwargs): catalog = ndb() st = time() # Extract parameters from keyword arguments s_id = kwargs.get('s_id', None) source, destination = self.get_address_params(kwargs) requirements = kwargs.get('requirements', None) # Append it to the list of service IDs self.s_ids[s_id] = requirements # Get network topology (topo_success, topo_msg) = self.ovs_ctl.get_topology() if not topo_success: # Send error message msg = '[ERROR]: Could not retrieve the network topology from ovs controller' print('failed', (time() - st) * 1000, 'ms') # Inform the user about the creation return False, msg topology = topo_msg.get('topology') catalog.set_topology(topology) # Define the route which can support the required QoS route = self.build_route(topology, source, destination, requirements) if route is None: # Send error message msg = '[WARN]: There is no available path for source ' + str( source) + ' and destination ' + str( destination) + ' supporting the follow QoS: ' + str( requirements) print('failed', (time() - st) * 1000, 'ms') # Inform the user about the creation return False, msg # Send message to OVS SDN controller self._log('Delegating it to the OVS Controller') # Send the message to create a slice success, msg = self.ovs_ctl.create_slice(**{ 's_id': s_id, 'route': route }) print('success', (time() - st) * 1000, 'ms') if success: catalog.add_route(s_id, route) # Inform the user about the creation return success, msg
def build_route(self, topology, src, dst, requirements): catalog = ndb() engine = PathEngine() # Fetch switches which can arrive to the src and dst networks src_network = self.find_border_switch(src) dst_network = self.find_border_switch(dst) if src_network is None or dst_network is None: print('\t', 'Impossible to arrive from ', src, 'to ', dst) return None # Define the path to apply path = engine.get_path(topology, src_network.get('switch'), dst_network.get('switch'), requirements) print('path ', path) if path is None: return None print('\t', 'Path to be applied: ', path) (ipv4_src, ipv4_src_netmask) = self.convert_cidr_to_netmask(src) (ipv4_dst, ipv4_dst_netmask) = self.convert_cidr_to_netmask(dst) (min_rate, max_rate, priority) = self.define_queue_parameters(requirements) first_port = src_network.get('port') last_port = dst_network.get('port') switches = engine.generate_match_switches(topology, path, first_port, last_port) path_string = '-'.join(map(str, path)) route = { 'src': src, 'dst': dst, 'ipv4_src': ipv4_src, 'ipv4_src_netmask': ipv4_src_netmask, 'ipv4_dst': ipv4_dst, 'ipv4_dst_netmask': ipv4_dst_netmask, 'min_rate': min_rate, 'max_rate': max_rate, 'priority': priority, 'switches': switches, 'path_string': path_string, 'path': path, 'latency': requirements.get('latency'), 'throughput': requirements.get('throughput') } return route
def configure_metric_paths(self, request): catalog = ndb() src_agent = catalog.get_local_agent(request.get('name')) configured_agents = catalog.get_configured_agents() configured_agent = None if src_agent.get('name') not in configured_agents: dst = [] agents = catalog.get_local_agents() for dst_agent_name in agents: if src_agent.get('name') != dst_agent_name: src = self.configure_flow_rules(src_agent, agents[dst_agent_name]) if src is None: return None dst.append(agents[dst_agent_name].get('host')) configured_agent = catalog.add_configured_agent( src_agent, src, dst) return configured_agent
def get_configuration(self, request): catalog = ndb() agents = catalog.get_local_agents() t_id = request.get('t_id') name = request.get('name') if name not in agents: return self.error_resp(t_id, 1) configured_agent = self.configure_metric_paths(request) if configured_agent is not None: resp = { "t_id": t_id, "type": "config_resp", "src": configured_agent.get('src'), "dst": configured_agent.get('dst'), "management_iface": agents[name].get('management_iface'), "result_code": 0 } else: resp = self.error_resp(t_id, 2) return resp
def post_init(self, **kwargs): # OVS Controller Handler self.ovs_ctl = ctl_base(name="OVS", host_key="ovs_host", port_key="ovs_port", default_host="20.1.0.1", default_port="3200", request_key="ovs_req", reply_key="ovs_rep", create_msg='ovc_crs', request_msg='ovc_rrs', update_msg='ovc_urs', delete_msg='ovc_drs', topology_msg='ovc_trs') # setting link speeds manually # TODO: to create a service to fetch these values automatically from ovsdb or ofconfig catalog = ndb() catalog.set_link_capacity('s01', 's02', 1000) catalog.set_link_capacity('s01', 's04', 1000) catalog.set_link_capacity('s02', 's03', 1000) catalog.set_link_capacity('s02', 's01', 1000) catalog.set_link_capacity('s03', 's04', 1000) catalog.set_link_capacity('s03', 's02', 1000) catalog.set_link_capacity('s04', 's01', 1000) catalog.set_link_capacity('s04', 's03', 1000) ''' Setting known hosts and networks manually. It could be automatic if we develop LLDP and ARP functions in the ovs controller... ... but it is out of scope. ''' # catalog.add_network('30.0.1.0/24', 's01', 4) # catalog.add_network('10.0.4.0/24', 's01', 4) catalog.add_network('30.0.5.0/24', 's01', 3) catalog.add_network('30.0.6.0/24', 's01', 4) catalog.add_network('30.0.7.0/24', 's01', 5) catalog.add_network('10.0.0.0/24', 's03', 3)
def get(self): catalog = ndb() topology = catalog.get_topology() capacity = catalog.get_capacity() routes = catalog.get_routes() networks = catalog.get_networks() usage = catalog.get_usage() flows = catalog.get_flows() local_agents = catalog.get_local_agents() configured_agents = catalog.get_configured_agents() path_latency = catalog.get_path_latencies() virtual_ifaces = catalog.get_virtual_ifaces() return { "topology": topology, "capacity": capacity, "routes": routes, "networks": networks, "usage": usage, "flows": flows, "local_agents": local_agents, "configured_agents": configured_agents, "path_latency": path_latency, "virtual_ifaces": virtual_ifaces }
def get_capable_path(self, paths, requirements): print('requirements', requirements) catalog = ndb() throughput = 0 if requirements is not None: # If our platform can support other QoS in the future, please add them as 'if' below: if requirements.get('latency') is not None: print('latency qos') paths = self.get_latency_comply_paths(paths, requirements.get('latency')) print('paths', paths) if requirements.get('throughput') is not None: print('throughput qos') throughput = requirements.get('throughput') path = self.get_throughput_comply_path(paths, throughput) else: if len(paths) > 0: path = min(paths, key=len) else: path = None if path is not None: for p in range(0, len(path) - 1): catalog.add_link_usage(path[p], path[p + 1], throughput) catalog.add_flow_count(path[p], path[p + 1], 1) return path
def get(self): catalog = ndb() routes = catalog.get_routes() return {'routes': routes}