diff --git a/ryu/lib/of_config/capable_switch.py b/ryu/lib/of_config/capable_switch.py new file mode 100644 index 00000000..b067da0f --- /dev/null +++ b/ryu/lib/of_config/capable_switch.py @@ -0,0 +1,110 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2013 Isaku Yamahata +# +# 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. + +import ncclient +import ncclient.manager +import ncclient.xml_ + +from ryu import exception as ryu_exc +from ryu.lib import of_config +from ryu.lib.of_config import constants as ofc_consts + + +# TODO: When we re-organize ncclient, its NCClientError will be +# subclass of RyuException. +class OFConfigCapableSwitchNotFound(ryu_exc.RyuException, + ncclient.NCClientError): + message = 'OpenFlow Capable Switch not found' + + +def get_ns_tag(tag): + if tag[0] == '{': + return tuple(tag[1:].split('}', 1)) + return (None, tag) + + +class OFCapableSwitch(object): + def __init__(self, connect_method='connect_ssh', *args, **kwargs): + super(OFCapableSwitch, self).__init__() + self._connect_method = connect_method + self._connect_args = args + self._connect_kwargs = kwargs + self.version = None + self.namespace = None + + connect = getattr(ncclient.manager, self._connect_method) + self.netconf = connect(*self._connect_args, **self._connect_kwargs) + + def close_session(self): + if self.netconf: + self.netconf.close_session() + self.netconf = None + + def __enter__(self): + return self + + def __exit__(self): + self.close_session() + + def client_capabilities(self): + return self.netconf.client_capabilities + + def server_capabilities(self): + return self.netconf.server_capabilities + + def _find_capable_switch(self, tree): + capable_switch = None + for element in tree.getchildren(): + ns, tag = get_ns_tag(element.tag) + if tag != ofc_consts.CAPABLE_SWITCH: + continue + + # assumes that returns only single capable switch + assert capable_switch is None + + capable_switch = element + if not self.version: + versions = [(version, ns_) for version, ns_ in + of_config.OFCONFIG_YANG_NAMESPACES.items() + if ns == ns_] + if versions: + assert len(versions) == 1 + version = versions[0] + self.version, self.namespace = version + + if not capable_switch: + raise OFConfigCapableSwitchNotFound() + + return capable_switch + + def _find_capable_switch_xml(self, tree): + return ncclient.xml_.to_xml(self._find_capable_switch(tree)) + + def get(self, filter=None): + reply = self.netconf.get(filter) + return self._find_capable_switch_xml(reply.data_ele) + + def get_config(self, source, filter=None): + reply = self.netconf.get_config(source, filter) + return self._find_capable_switch_xml(reply.data_ele) + + def edit_config(self, target, config, default_operation=None, + test_option=None, error_option=None): + self.netconf.edit_config(target, config, + default_operation, test_option, error_option) + + # TODO: more netconf operations + # TODO: convinience(higher level) methods