diff options
-rw-r--r-- | ryu/tests/integrated/test_of_config.py | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/ryu/tests/integrated/test_of_config.py b/ryu/tests/integrated/test_of_config.py new file mode 100644 index 00000000..a8c4f9ff --- /dev/null +++ b/ryu/tests/integrated/test_of_config.py @@ -0,0 +1,229 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# Copyright (C) 2013 Isaku Yamahata <yamahata at private email ne jp> +# +# 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 gevent +import traceback + +import lxml.etree +import ncclient + +from ryu.base import app_manager +from ryu.lib.netconf import constants as nc_consts +from ryu.lib import of_config +from ryu.lib.of_config import capable_switch +from ryu.lib.of_config import constants as ofc_consts + + +# Change those depending on switch configuration +HOST = '127.0.0.1' +PORT = 1830 +USERNAME = 'linc' +PASSWORD = 'linc' + +CAPABLE_SWITCH_ID = 'CapableSwitch0' +LOGICAL_SWITCH = 'LogicalSwitch0' +PORT_ID = 'LogicalSwitch0-Port2' +CONTROLLER_ID = 'Switch0-DefaultController' + +PORT_DICT = { + 'capable_switch': CAPABLE_SWITCH_ID, + 'port_id': PORT_ID, + 'logical_switch': LOGICAL_SWITCH, + 'controller_id': CONTROLLER_ID, + 'ip': HOST, +} + +SWITCH_PORT_DOWN = ''' +<nc:config xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <capable-switch xmlns="urn:onf:of111:config:yang"> + <id>%(capable_switch)s</id> + <resources> + <port> + <resource-id>%(port_id)s</resource-id> + <configuration operation="merge"> + <admin-state>down</admin-state> + <no-receive>false</no-receive> + <no-forward>false</no-forward> + <no-packet-in>false</no-packet-in> + </configuration> + </port> + </resources> + </capable-switch> +</nc:config> +''' % PORT_DICT + +SWITCH_ADVERTISED = ''' +<nc:config xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <capable-switch xmlns="urn:onf:of111:config:yang"> + <id>%(capable_switch)s</id> + <resources> + <port> + <resource-id>%(port_id)s</resource-id> + <features> + <advertised operation="merge"> + <rate>10Mb-FD</rate> + <auto-negotiate>true</auto-negotiate> + <medium>copper</medium> + <pause>unsupported</pause> + </advertised> + </features> + </port> + </resources> + </capable-switch> +</nc:config> +''' % PORT_DICT + +SWITCH_CONTROLLER = ''' +<nc:config xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <capable-switch xmlns="urn:onf:of111:config:yang"> + <id>%(capable_switch)s</id> + <logical-switches> + <switch> + <id>%(logical_switch)s</id> + <controllers> + <controller operation="merge"> + <id>%(controller_id)s</id> + <role>master</role> + <ip-address>%(ip)s</ip-address> + <port>6633</port> + <protocol>tcp</protocol> + </controller> + </controllers> + </switch> + </logical-switches> + </capable-switch> +</nc:config> +''' % PORT_DICT + + +def _get_schema(): + # file_name = of_config.OF_CONFIG_1_0_XSD + # file_name = of_config.OF_CONFIG_1_1_XSD + file_name = of_config.OF_CONFIG_1_1_1_XSD + return lxml.etree.XMLSchema(file=file_name) + + +class OFConfigClient(app_manager.RyuApp): + def __init__(self, *args, **kwargs): + super(OFConfigClient, self).__init__(*args, **kwargs) + self.switch = capable_switch.OFCapableSwitch( + host=HOST, port=PORT, username=USERNAME, password=PASSWORD) + gevent.spawn(self._do_of_config) + + def _validate(self, tree): + xmlschema = _get_schema() + try: + xmlschema.assertValid(tree) + except: + traceback.print_exc() + + def _do_get(self): + data_xml = self.switch.get() + + tree = lxml.etree.fromstring(data_xml) + # print(lxml.etree.tostring(tree, pretty_print=True)) + self._validate(tree) + + name_spaces = set() + for e in tree.getiterator(): + name_spaces.add(capable_switch.get_ns_tag(e.tag)[0]) + print(name_spaces) + + return tree + + def _do_get_config(self, source): + print('source = %s' % source) + config_xml = self.switch.get_config(source) + + tree = lxml.etree.fromstring(config_xml) + # print(lxml.etree.tostring(tree, pretty_print=True)) + self._validate(tree) + + def _do_edit_config(self, config): + tree = lxml.etree.fromstring(config) + self._validate(tree) + self.switch.edit_config(target='running', config=config) + + def _print_ports(self, tree, ns): + for port in tree.findall('{%s}%s/{%s}%s' % (ns, ofc_consts.RESOURCES, + ns, ofc_consts.PORT)): + print(lxml.etree.tostring(port, pretty_print=True)) + + def _set_ports_down(self): + """try to set all ports down with etree operation""" + tree = self._do_get() + print lxml.etree.tostring(tree, pretty_print=True) + + qname = lxml.etree.QName(tree.tag) + ns = qname.namespace + self._print_ports(tree, ns) + + switch_id = tree.find('{%s}%s' % (ns, ofc_consts.ID)) + resources = tree.find('{%s}%s' % (ns, ofc_consts.RESOURCES)) + configuration = tree.find( + '{%s}%s/{%s}%s/{%s}%s' % (ns, ofc_consts.RESOURCES, + ns, ofc_consts.PORT, + ns, ofc_consts.CONFIGURATION)) + admin_state = tree.find( + '{%s}%s/{%s}%s/{%s}%s/{%s}%s' % (ns, ofc_consts.RESOURCES, + ns, ofc_consts.PORT, + ns, ofc_consts.CONFIGURATION, + ns, ofc_consts.ADMIN_STATE)) + + config_ = lxml.etree.Element( + '{%s}%s' % (ncclient.xml_.BASE_NS_1_0, nc_consts.CONFIG)) + capable_switch_ = lxml.etree.SubElement(config_, tree.tag) + switch_id_ = lxml.etree.SubElement(capable_switch_, switch_id.tag) + switch_id_.text = switch_id.text + resources_ = lxml.etree.SubElement(capable_switch_, + resources.tag) + for port in tree.findall( + '{%s}%s/{%s}%s' % (ns, ofc_consts.RESOURCES, + ns, ofc_consts.PORT)): + resource_id = port.find('{%s}%s' % (ns, ofc_consts.RESOURCE_ID)) + + port_ = lxml.etree.SubElement(resources_, port.tag) + resource_id_ = lxml.etree.SubElement(port_, resource_id.tag) + resource_id_.text = resource_id.text + configuration_ = lxml.etree.SubElement(port_, configuration.tag) + configuration_.set(ofc_consts.OPERATION, nc_consts.MERGE) + admin_state_ = lxml.etree.SubElement(configuration_, + admin_state.tag) + admin_state_.text = ofc_consts.DOWN + self._do_edit_config(lxml.etree.tostring(config_, pretty_print=True)) + + tree = self._do_get() + self._print_ports(tree, ns) + + def _do_of_config(self): + self._do_get() + self._do_get_config('running') + self._do_get_config('startup') + + # LINC doesn't support 'candidate' datastore + try: + self._do_get_config('candidate') + except ncclient.NCClientError: + traceback.print_exc() + + # use raw XML format + self._do_edit_config(SWITCH_PORT_DOWN) + self._do_edit_config(SWITCH_ADVERTISED) + self._do_edit_config(SWITCH_CONTROLLER) + + self._set_ports_down() + + self.switch.close_session() |