def get_netconf_protocol(self): self.last_connection_id += 1 return NetconfProtocol( datastore=self.datastore, capabilities=self.capabilities(), additionnal_namespaces={"junos": NS_JUNOS}, logger=logging.getLogger( "fake_switches.juniper.%s.%s.netconf" % (self.switch_configuration.name, self.last_connection_id)))
def get_netconf_protocol(self): self.last_connection_id += 1 return NetconfProtocol( datastore=self.datastore, capabilities=[ Candidate1_0, ConfirmedCommit1_0, Validate1_0, Url1_0, NetconfJunos1_0, DmiSystem1_0 ], additionnal_namespaces={"junos": NS_JUNOS}, logger=logging.getLogger( "fake_switches.juniper.%s.%s.netconf" % (self.switch_configuration.name, self.last_connection_id)))
def setUp(self): self.netconf = NetconfProtocol(logger=logging.getLogger()) self.netconf.transport = Mock()
class NetconfProtocolTest(unittest.TestCase): def setUp(self): self.netconf = NetconfProtocol(logger=logging.getLogger()) self.netconf.transport = Mock() def test_says_hello_upon_connection_and_receive_an_hello(self): self.netconf.connectionMade() self.assert_xml_response(""" <hello> <session-id>1</session-id> <capabilities> <capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability> </capabilities> </hello> """) def test_close_session_support(self): self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived( '<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="12345">\n' ) self.netconf.dataReceived(' <nc:close-session/>\n') self.netconf.dataReceived('</nc:rpc>\n') self.netconf.dataReceived(']]>]]>\n') self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="12345"> <ok/> </rpc-reply> """) self.netconf.transport.loseConnection.assert_called_with() def test_get_config_support(self): self.netconf.datastore.set_data( RUNNING, {"configuration": { "stuff": "is cool!" }}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <get-config> <source><running /></source> </get-config> </rpc> ]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data> <configuration> <stuff>is cool!</stuff> </configuration> </data> </rpc-reply> """) assert_that(self.netconf.transport.loseConnection.called, equal_to(False)) def test_request_with_namespace(self): self.netconf.datastore.set_data( RUNNING, {"configuration": { "stuff": "is cool!" }}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <nc:get-config> <nc:source><nc:running/></nc:source> </nc:get-config> </nc:rpc> ]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data> <configuration> <stuff>is cool!</stuff> </configuration> </data> </rpc-reply> """) def test_edit_config(self): self.netconf.datastore.set_data( RUNNING, {"configuration": { "stuff": { "substuff": "is cool!" } }}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived("""<?xml version="1.0" encoding="UTF-8"?> <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:346c9f18-c420-11e4-8e4c-fa163ecd3b0a"> <nc:edit-config> <nc:target> <nc:candidate/> </nc:target> <nc:config> <nc:configuration><nc:stuff><substuff>is hot!</substuff></nc:stuff></nc:configuration> </nc:config> </nc:edit-config> </nc:rpc>]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:346c9f18-c420-11e4-8e4c-fa163ecd3b0a"> <ok/> </rpc-reply> """) def test_reply_includes_additional_namespaces(self): self.netconf.additionnal_namespaces = { "junos": "http://xml.juniper.net/junos/11.4R1/junos", "nc": "urn:ietf:params:xml:ns:netconf:base:1.0", } self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <nc:get-config> <nc:source><nc:running/></nc:source> </nc:get-config> </nc:rpc> ]]>]]>""") assert_that( self.netconf.transport.write.call_args[0][0], xml_equals_to(""" <rpc-reply xmlns:junos="http://xml.juniper.net/junos/11.4R1/junos" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data/> </rpc-reply> ]]>]]> """)) def test_filtering(self): content = dict_2_etree({ "data": { "configuration": [{ "shizzle": { "whizzle": {} } }, { "shizzle": { "whizzle": { "howdy": {} }, "not-whizzle": { "not-howdy": {} } } }, { "zizzle": { "nothing": {} } }, { "outzzle": { "nothing": {} } }] } }) content_filter = dict_2_etree({ "filter": { "configuration": { "shizzle": { "whizzle": {} }, "zizzle": {}, } } }) filter_content(content, content_filter) assert_that(content.xpath("//data/configuration/shizzle"), has_length(2)) assert_that(content.xpath("//data/configuration/shizzle/*"), has_length(2)) assert_that( content.xpath("//data/configuration/shizzle/whizzle/howdy"), has_length(1)) assert_that(content.xpath("//data/configuration/zizzle"), has_length(1)) assert_that(content.xpath("//data/configuration/outzzle"), has_length(0)) def test_filtering_with_a_value(self): content = dict_2_etree({ "data": { "configuration": [ { "element": { "element-key": "MY-KEY", "attribute": { "sub-attribute": {} } } }, { "element": { "element-key": "MY-OTHER-KEY", "other-attribute": { "sub-attribute": {} } } }, ] } }) content_filter = dict_2_etree({ "filter": { "configuration": { "element": { "element-key": "MY-KEY" }, } } }) filter_content(content, content_filter) assert_that(content.xpath("//data/configuration/element"), has_length(1)) assert_that(content.xpath("//data/configuration/element/*"), has_length(2)) assert_that(content.xpath("//data/configuration/element/attribute/*"), has_length(1)) def say_hello(self): self.netconf.dataReceived( '<hello xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><capabilities><capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability></capabilities></hello>]]>]]>' ) def assert_xml_response(self, expected): data = self.netconf.transport.write.call_args[0][0] assert_that(data, ends_with("]]>]]>\n")) data = data.replace("]]>]]>", "") assert_that(data, xml_equals_to(expected))
class NetconfProtocolTest(unittest.TestCase): def setUp(self): self.netconf = NetconfProtocol(logger=logging.getLogger()) self.netconf.transport = Mock() def test_says_hello_upon_connection_and_receive_an_hello(self): self.netconf.connectionMade() self.assert_xml_response(""" <hello> <session-id>1</session-id> <capabilities> <capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability> </capabilities> </hello> """) def test_close_session_support(self): self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived('<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="12345">\n') self.netconf.dataReceived(' <nc:close-session/>\n') self.netconf.dataReceived('</nc:rpc>\n') self.netconf.dataReceived(']]>]]>\n') self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="12345"> <ok/> </rpc-reply> """) self.netconf.transport.loseConnection.assert_called_with() def test_get_config_support(self): self.netconf.datastore.set_data(RUNNING, {"configuration": {"stuff": "is cool!"}}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <get-config> <source><running /></source> </get-config> </rpc> ]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data> <configuration> <stuff>is cool!</stuff> </configuration> </data> </rpc-reply> """) assert_that(self.netconf.transport.loseConnection.called, equal_to(False)) def test_request_with_namespace(self): self.netconf.datastore.set_data(RUNNING, {"configuration": {"stuff": "is cool!"}}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <nc:get-config> <nc:source><nc:running/></nc:source> </nc:get-config> </nc:rpc> ]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data> <configuration> <stuff>is cool!</stuff> </configuration> </data> </rpc-reply> """) def test_edit_config(self): self.netconf.datastore.set_data(RUNNING, {"configuration": {"stuff": {"substuff": "is cool!"}}}) self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived("""<?xml version="1.0" encoding="UTF-8"?> <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:346c9f18-c420-11e4-8e4c-fa163ecd3b0a"> <nc:edit-config> <nc:target> <nc:candidate/> </nc:target> <nc:config> <nc:configuration><nc:stuff><substuff>is hot!</substuff></nc:stuff></nc:configuration> </nc:config> </nc:edit-config> </nc:rpc>]]>]]>""") self.assert_xml_response(""" <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:346c9f18-c420-11e4-8e4c-fa163ecd3b0a"> <ok/> </rpc-reply> """) def test_reply_includes_additional_namespaces(self): self.netconf.additionnal_namespaces = { "junos": "http://xml.juniper.net/junos/11.4R1/junos", "nc": "urn:ietf:params:xml:ns:netconf:base:1.0", } self.netconf.connectionMade() self.say_hello() self.netconf.dataReceived(""" <nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <nc:get-config> <nc:source><nc:running/></nc:source> </nc:get-config> </nc:rpc> ]]>]]>""") assert_that(self.netconf.transport.write.call_args[0][0], xml_equals_to(""" <rpc-reply xmlns:junos="http://xml.juniper.net/junos/11.4R1/junos" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="67890"> <data/> </rpc-reply> ]]>]]> """ )) def test_filtering(self): content = dict_2_etree({ "data": { "configuration": [ {"shizzle": { "whizzle": {} }}, {"shizzle": { "whizzle": { "howdy": {} }, "not-whizzle": { "not-howdy": {} } }}, {"zizzle": { "nothing": {} }}, {"outzzle": { "nothing": {} }} ] } }) content_filter = dict_2_etree({ "filter": { "configuration": { "shizzle": {"whizzle": {}}, "zizzle": {}, } } }) filter_content(content, content_filter) assert_that(content.xpath("//data/configuration/shizzle"), has_length(2)) assert_that(content.xpath("//data/configuration/shizzle/*"), has_length(2)) assert_that(content.xpath("//data/configuration/shizzle/whizzle/howdy"), has_length(1)) assert_that(content.xpath("//data/configuration/zizzle"), has_length(1)) assert_that(content.xpath("//data/configuration/outzzle"), has_length(0)) def test_filtering_with_a_value(self): content = dict_2_etree({ "data": { "configuration": [ {"element": { "element-key": "MY-KEY", "attribute": {"sub-attribute": {}} }}, {"element": { "element-key": "MY-OTHER-KEY", "other-attribute": {"sub-attribute": {}} }}, ] } }) content_filter = dict_2_etree({ "filter": { "configuration": { "element": { "element-key": "MY-KEY" }, } } }) filter_content(content, content_filter) assert_that(content.xpath("//data/configuration/element"), has_length(1)) assert_that(content.xpath("//data/configuration/element/*"), has_length(2)) assert_that(content.xpath("//data/configuration/element/attribute/*"), has_length(1)) def say_hello(self): self.netconf.dataReceived( '<hello xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"><capabilities><capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability></capabilities></hello>]]>]]>') def assert_xml_response(self, expected): data = self.netconf.transport.write.call_args[0][0] assert_that(data, ends_with("]]>]]>\n")) data = data.replace("]]>]]>", "") assert_that(data, xml_equals_to(expected))