| # -*- coding: utf-8 -*- |
| |
| ## |
| # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U. |
| # This file is part of openmano |
| # All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| # |
| # For those usages not covered by the Apache License, Version 2.0 please |
| # contact with: nfvlabs@tid.es |
| ## |
| import logging |
| import base64 |
| |
| """ |
| vimconn implement an Abstract class for the vim connector plugins |
| with the definition of the method to be implemented. |
| """ |
| __author__ = "Alfonso Tierno, Leonardo Mirabal" |
| __date__ = "$16-oct-2015 11:09:29$" |
| |
| |
| |
| # Error variables |
| HTTP_Bad_Request = 400 |
| HTTP_Unauthorized = 401 |
| HTTP_Not_Found = 404 |
| HTTP_Method_Not_Allowed = 405 |
| HTTP_Request_Timeout = 408 |
| HTTP_Conflict = 409 |
| HTTP_Not_Implemented = 501 |
| HTTP_Service_Unavailable = 503 |
| HTTP_Internal_Server_Error = 500 |
| |
| |
| class OpenflowconnException(Exception): |
| """Common and base class Exception for all vimconnector exceptions""" |
| def __init__(self, message, http_code=HTTP_Bad_Request): |
| Exception.__init__(self, message) |
| self.http_code = http_code |
| |
| |
| class OpenflowconnConnectionException(OpenflowconnException): |
| """Connectivity error with the VIM""" |
| def __init__(self, message, http_code=HTTP_Service_Unavailable): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnUnexpectedResponse(OpenflowconnException): |
| """Get an wrong response from VIM""" |
| def __init__(self, message, http_code=HTTP_Internal_Server_Error): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnAuthException(OpenflowconnException): |
| """Invalid credentials or authorization to perform this action over the VIM""" |
| def __init__(self, message, http_code=HTTP_Unauthorized): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnNotFoundException(OpenflowconnException): |
| """The item is not found at VIM""" |
| def __init__(self, message, http_code=HTTP_Not_Found): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnConflictException(OpenflowconnException): |
| """There is a conflict, e.g. more item found than one""" |
| def __init__(self, message, http_code=HTTP_Conflict): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnNotSupportedException(OpenflowconnException): |
| """The request is not supported by connector""" |
| def __init__(self, message, http_code=HTTP_Service_Unavailable): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowconnNotImplemented(OpenflowconnException): |
| """The method is not implemented by the connected""" |
| def __init__(self, message, http_code=HTTP_Not_Implemented): |
| OpenflowconnException.__init__(self, message, http_code) |
| |
| |
| class OpenflowConn: |
| """ |
| Openflow controller connector abstract implementeation. |
| """ |
| def __init__(self, params): |
| self.name = "openflow_conector" |
| self.headers = {'content-type': 'application/json', 'Accept': 'application/json'} |
| self.auth = None |
| self.pp2ofi = {} # From Physical Port to OpenFlow Index |
| self.ofi2pp = {} # From OpenFlow Index to Physical Port |
| self.dpid = '00:01:02:03:04:05:06:07' |
| self.id = 'openflow:00:01:02:03:04:05:06:07' |
| self.rules = {} |
| self.url = "http://%s:%s" % ('localhost', str(8081)) |
| self.auth = base64.b64encode('of_user:of_password') |
| self.headers['Authorization'] = 'Basic ' + self.auth |
| self.logger = logging.getLogger('openflow_conn') |
| self.logger.setLevel(getattr(logging, params.get("of_debug", "ERROR"))) |
| self.ip_address = None |
| |
| def get_of_switches(self): |
| """" |
| Obtain a a list of switches or DPID detected by this controller |
| :return: list length, and a list where each element a tuple pair (DPID, IP address), text_error: if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| def obtain_port_correspondence(self): |
| """ |
| Obtain the correspondence between physical and openflow port names |
| :return: dictionary: with physical name as key, openflow name as value, error_text: if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| def get_of_rules(self, translate_of_ports=True): |
| """ |
| Obtain the rules inserted at openflow controller |
| :param translate_of_ports: if True it translates ports from openflow index to physical switch name |
| :return: dict if ok: with the rule name as key and value is another dictionary with the following content: |
| priority: rule priority |
| name: rule name (present also as the master dict key) |
| ingress_port: match input port of the rule |
| dst_mac: match destination mac address of the rule, can be missing or None if not apply |
| vlan_id: match vlan tag of the rule, can be missing or None if not apply |
| actions: list of actions, composed by a pair tuples: |
| (vlan, None/int): for stripping/setting a vlan tag |
| (out, port): send to this port |
| switch: DPID, all |
| text_error if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| def del_flow(self, flow_name): |
| """ |
| Delete all existing rules |
| :param flow_name: flow_name, this is the rule name |
| :return: None if ok, text_error if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| def new_flow(self, data): |
| """ |
| Insert a new static rule |
| :param data: dictionary with the following content: |
| priority: rule priority |
| name: rule name |
| ingress_port: match input port of the rule |
| dst_mac: match destination mac address of the rule, missing or None if not apply |
| vlan_id: match vlan tag of the rule, missing or None if not apply |
| actions: list of actions, composed by a pair tuples with these posibilities: |
| ('vlan', None/int): for stripping/setting a vlan tag |
| ('out', port): send to this port |
| :return: None if ok, text_error if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| def clear_all_flows(self): |
| """" |
| Delete all existing rules |
| :return: None if ok, text_error if fails |
| """ |
| raise OpenflowconnNotImplemented("Should have implemented this") |
| |
| |
| class OfTestConnector(OpenflowConn): |
| """ |
| This is a fake openflow connector for testing. |
| It does nothing and it is used for running openvim without an openflow controller |
| """ |
| |
| def __init__(self, params): |
| OpenflowConn.__init__(self, params) |
| |
| name = params.get("name", "test-ofc") |
| self.name = name |
| self.dpid = params.get("dpid") |
| self.rules = {} |
| self.logger = logging.getLogger('vim.OF.TEST') |
| self.logger.setLevel(getattr(logging, params.get("of_debug", "ERROR"))) |
| self.pp2ofi = {} |
| |
| def get_of_switches(self): |
| return () |
| |
| def obtain_port_correspondence(self): |
| return () |
| |
| def del_flow(self, flow_name): |
| if flow_name in self.rules: |
| self.logger.debug("del_flow OK") |
| del self.rules[flow_name] |
| return None |
| else: |
| self.logger.warning("del_flow not found") |
| raise OpenflowconnUnexpectedResponse("flow {} not found".format(flow_name)) |
| |
| def new_flow(self, data): |
| self.rules[data["name"]] = data |
| self.logger.debug("new_flow OK") |
| return None |
| |
| def get_of_rules(self, translate_of_ports=True): |
| return self.rules |
| |
| def clear_all_flows(self): |
| self.logger.debug("clear_all_flows OK") |
| self.rules = {} |
| return None |