6

[編集:Python2.7.3を実行しています]

私は貿易によるネットワークエンジニアであり、BrocadeのNETCONFの実装で動作させるために、 ncclient(Webサイトのバージョンは古く、これは私が取り組んできたバージョンでした)をハッキングしてきました。Brocade機器で動作させるためにいくつかの調整を行う必要がありましたが、パッケージをフォークしてソース自体に調整を加える必要がありました。これは私には「クリーン」とは感じられなかったので、「正しい方法」でそれを実行し、パッケージに存在するいくつかのものをオーバーライドしたいと思いました*。具体的には3つのこと:

  1. それ自体がSessionListenerのサブクラスであるHelloHandlerクラスに属するbuild()と呼ばれる「静的メソッド」
  2. RPCクラスの"._id"属性(元の実装ではuuidが使用されていましたが、Brocadeボックスはこれをあまり好まなかったため、元の調整では、これを変更されない静的な値に変更しました)。
  3. XMLフィルター属性を構築するutil関数の微調整

これまでのところ、私はこのコードをファイルに入れていますbrcd_ncclient.py

#!/usr/bin/env python

# hack on XML element creation and create a subclass to override HelloHandler's
# build() method to format the XML in a way that the brocades actually like

from ncclient.xml_ import *
from ncclient.transport.session import HelloHandler
from ncclient.operations.rpc import RPC, RaiseMode
from ncclient.operations import util

# register brocade namespace and create functions to create proper xml for
# hello/capabilities exchange

BROCADE_1_0 = "http://brocade.com/ns/netconf/config/netiron-config/"
register_namespace('brcd', BROCADE_1_0)

brocade_new_ele = lambda tag, ns, attrs={}, **extra: ET.Element(qualify(tag, ns), attrs, **extra)

brocade_sub_ele = lambda parent, tag, ns, attrs={}, **extra: ET.SubElement(parent, qualify(tag, ns), attrs, **extra)

# subclass RPC to override self._id to change uuid-generated message-id's;
# Brocades seem to not be able to handle the really long id's
class BrcdRPC(RPC):
    def __init__(self, session, async=False, timeout=30, raise_mode=RaiseMode.NONE):
        self._id = "1"
        return super(BrcdRPC, self).self._id

class BrcdHelloHandler(HelloHandler):
    def __init__(self):
        return super(BrcdHelloHandler, self).__init__()

    @staticmethod
    def build(capabilities):
        hello = brocade_new_ele("hello", None, {'xmlns':"urn:ietf:params:xml:ns:netconf:base:1.0"})
        caps = brocade_sub_ele(hello, "capabilities", None)
        def fun(uri): brocade_sub_ele(caps, "capability", None).text = uri
        map(fun, capabilities)
        return to_xml(hello)
        #return super(BrcdHelloHandler, self).build() ???

# since there's no classes I'm assuming I can just override the function itself
# in ncclient.operations.util?
def build_filter(spec, capcheck=None):
    type = None
    if isinstance(spec, tuple):
        type, criteria = spec
        # brocades want the netconf prefix on subtree filter attribute
        rep = new_ele("filter", {'nc:type':type})
        if type == "xpath":
            rep.attrib["select"] = criteria
        elif type == "subtree":
            rep.append(to_ele(criteria))
        else:
            raise OperationError("Invalid filter type")
    else:
        rep = validated_element(spec, ("filter", qualify("filter")),
                                    attrs=("type",))
        # TODO set type var here, check if select attr present in case of xpath..
    if type == "xpath" and capcheck is not None:
        capcheck(":xpath")
    return rep

そして、私のファイルnetconftest.pyには次のようなものがあります。

#!/usr/bin/env python

from ncclient import manager
from brcd_ncclient import *

manager.logging.basicConfig(filename='ncclient.log', level=manager.logging.DEBUG)

# brocade server capabilities advertising as 1.1 compliant when they're really not
# this will stop ncclient from attempting 1.1 chunked netconf message transactions
manager.CAPABILITIES = ['urn:ietf:params:netconf:capability:writeable-running:1.0', 'urn:ietf:params:netconf:base:1.0']

# BROCADE_1_0 is the namespace defined for netiron configs in brcd_ncclient
# this maps to the 'brcd' prefix used in xml elements, ie subtree filter criteria
with manager.connect(host='hostname_or_ip', username='username', password='password') as m:
    # 'get' request with no filter - for brocades just shows 'show version' data
    c = m.get()
    print c
    # 'get-config' request with 'mpls-config' filter - if no filter is
    # supplied with 'get-config', brocade returns nothing
    netironcfg = brocade_new_ele('netiron-config', BROCADE_1_0)
    mplsconfig = brocade_sub_ele(netironcfg, 'mpls-config', BROCADE_1_0)
    filterstr = to_xml(netironcfg)
    c2 = m.get_config(source='running', filter=('subtree', filterstr))
    print c2
    # so far it only looks like the supported filters for 'get-config'
    # operations are: 'interface-config', 'vlan-config' and 'mpls-config'

ファイルを実行するたびにnetconftest.py、タイムアウトエラーが発生します。これは、ログファイルncclient.logで、サブクラス定義(つまり、hello交換用のXMLを変更するもの-staticmethod build)が無視されており、Brocadeボックスがその方法を認識していないためです。元のncclientHelloHandler.build()メソッドが生成しているXMLを解釈します**。また、生成されたログファイルで、メッセージID(静的な値1)やXMLフィルターなど、オーバーライドしようとしている他のものも無視されていることがわかります。

だから、私はここでちょっと途方に暮れています。私は自分の研究からこのブログ投稿/モジュールを見つけました、そしてそれは私が望むことを正確に行うように見えます、しかし私はそれを使うのではなく手でそれをすることによって私が間違っていることを本当に理解できるようになりたいです誰かが私自身でこれを理解する必要がないための言い訳としてすでに書いたモジュール。

*これが「モンキーパッチ」で実際に悪いのかどうか誰かに説明してもらえますか?私の研究では、モンキーパッチは望ましくないことがわかりましたが、この回答この回答は私をかなり混乱させています。私にとって、これらのビットをオーバーライドしたいので、自分のncclientのフォーク全体を維持する必要がなくなります。

**もう少しコンテキストを与えるために、ncclient.transport.session.HelloHandler.build()デフォルトで生成されるこのXMLは、Brocadeボックスが気に入らないようです。

<?xml version='1.0' encoding='UTF-8'?>
    <nc:hello xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
        <nc:capabilities>
            <nc:capability>urn:ietf:params:netconf:base:1.0</nc:capability>   
            <nc:capability>urn:ietf:params:netconf:capability:writeable-running:1.0</nc:capability>   
        </nc:capabilities>
    </nc:hello>

私のオーバーライドさbuild()れたメソッドの目的は、上記のXMLをこれに変換することです(Brocadeは次のようにしています:

<?xml version="1.0" encoding="UTF-8"?>
    <hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
        <capabilities>
            <capability>urn:ietf:params:netconf:base:1.0</capability>
            <capability>urn:ietf:params:netconf:capability:writeable-running:1.0</capability>
        </capabilities>
    </hello>
4

1 に答える 1

2

ですから、「メタ情報」はそれほど急いで削除されるべきではなかったことがわかりました。繰り返しになりますが、私が聞きたいことを完全に理解していないと、私が求めているものに対する答えを見つけるのが難しいからです。私が本当にやりたかったのは、実行時にパッケージ内のものをオーバーライドすることでした。

これが私が変更brcd_ncclient.pyしたものです(簡潔にするためにコメントは削除されました):

#!/usr/bin/env python

from ncclient import manager
from ncclient.xml_ import *

brcd_new_ele = lambda tag, ns, attrs={}, **extra: ET.Element(qualify(tag, ns), attrs, **extra)
brcd_sub_ele = lambda parent, tag, ns, attrs={}, **extra: ET.SubElement(parent, qualify(tag, ns), attrs, **extra)

BROCADE_1_0 = "http://brocade.com/ns/netconf/config/netiron-config/"
register_namespace('brcd', BROCADE_1_0)

@staticmethod
def brcd_build(capabilities):
    hello = brcd_new_ele("hello", None, {'xmlns':"urn:ietf:params:xml:ns:netconf:base:1.0"})
    caps = brcd_sub_ele(hello, "capabilities", None)
    def fun(uri): brcd_sub_ele(caps, "capability", None).text = uri
    map(fun, capabilities)
    return to_xml(hello)

def brcd_build_filter(spec, capcheck=None):
    type = None
    if isinstance(spec, tuple):
        type, criteria = spec
        # brocades want the netconf prefix on subtree filter attribute
        rep = new_ele("filter", {'nc:type':type})
        if type == "xpath":
            rep.attrib["select"] = criteria
        elif type == "subtree":
            rep.append(to_ele(criteria))
        else:
            raise OperationError("Invalid filter type")
    else:
        rep = validated_element(spec, ("filter", qualify("filter")),
                                attrs=("type",))
    if type == "xpath" and capcheck is not None:
        capcheck(":xpath")
    return rep

manager.transport.session.HelloHandler.build = brcd_build
manager.operations.util.build_filter = brcd_build_filter

そしてnetconftest.py

#!/usr/bin/env python

from brcd_ncclient import *

manager.logging.basicConfig(filename='ncclient.log', level=manager.logging.DEBUG)

manager.CAPABILITIES = ['urn:ietf:params:netconf:capability:writeable-running:1.0', 'urn:ietf:params:netconf:base:1.0']

with manager.connect(host='host', username='user', password='password') as m:
    netironcfg = brcd_new_ele('netiron-config', BROCADE_1_0)
    mplsconfig = brcd_sub_ele(netironcfg, 'mpls-config', BROCADE_1_0)
    filterstr = to_xml(netironcfg)
    c2 = m.get_config(source='running', filter=('subtree', filterstr))
    print c2

これは私がなりたいところにほとんど私を連れて行きます。メッセージIDが生成されないように変更するには、元のソースコードを編集する必要があります。これは、実行時uuid1().urnにオブジェクトの属性を変更する方法がわからないか、理解していないためです__init__(鶏が先か卵が先か?)。これが問題のあるコードですncclient/operations/rpc.py

class RPC(object):
    DEPENDS = []
    REPLY_CLS = RPCReply
    def __init__(self, session, async=False, timeout=30, raise_mode=RaiseMode.NONE):
        self._session = session
        try:
            for cap in self.DEPENDS:
                self._assert(cap)
        except AttributeError:
            pass
        self._async = async
        self._timeout = timeout
        self._raise_mode = raise_mode
        self._id = uuid1().urn # Keeps things simple instead of having a class attr with running ID that has to be locked

私が本当にやりたかったことを最終的に手がかりにしたのは、ActiveStateのこのレシピにクレジットがあります。私が最初に投稿したコードは、技術的に間違っているとは思いません-私がやりたかったのが自分のncclientをフォークして変更したり、維持したりすることだった場合、それは私がやりたかったことではありませんでした、少なくとも今はそうではありません。

質問のタイトルを編集して、私が最初に望んでいたことをより適切に反映します。他の人々がより良いまたはよりクリーンなアイデアを持っている場合、私は完全にオープンです。

于 2013-01-06T23:04:23.023 に答える