2013-01-05 36 views
6

[编辑:我运行的Python 2.7.3]如何在运行时覆盖包中的东西?

我的职业是网络工程师,我一直黑客攻击ncclient(网站上的版本是旧的,并且this是我的版本一直在努力),使其能够与博科的NETCONF实施协同工作。为了使它能够与我们的博科设备一起工作,我不得不做出一些调整,但是我必须将包装分离并对源自身进行调整。这对我来说并不“干净”,所以我决定尝试以“正确的方式”来实现它,并覆盖包中存在的一些事物*;具体三两件事:

  1. A“静态方法”称为构建()属于HelloHandler类,这本身就是“._id” SessionListener
  2. 的子类的RPC类的属性(原来的实现使用uuid,博科盒子不太喜欢这个,所以在我原来的调整中,我只是将它改成了一个永不改变的静态值)。
  3. 一个小调整到一个实用程序功能构建XML过滤器属性

到目前为止,我有这样的代码在一个文件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中I可以看到我的子类定义(即更改用于hello交换的XML - 静态方法build)被忽略,Brocade框不知道如何解释原始ncclient方法正在生成的XML。我也可以在生成的日志文件中看到,我试图覆盖的其他内容也被忽略,如message-id(静态值为1)以及XML过滤器。

所以,我有点不知所措。我从我的研究中发现this blog post/module,并且它看起来完全符合我的要求,但我真的希望能够通过手动方式了解我做错了什么,而不是使用某人拥有的模块已经写成了一个借口,不必自己弄清楚这一点。

*有人可以向我解释,如果这是“猴子补丁”,实际上是坏的?我在我的研究中看到猴子补丁不可取,但this answerthis answer让我感到困惑。对我而言,我希望覆盖这些位会阻止我维护自己的ncclient的整个分支。

**为了让多一点背景下,这个XML,这ncclient.transport.session.HelloHandler.build()默认生成,博科盒子似乎并不喜欢:

<?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到这个(这博科确实喜欢:

<?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> 
+0

您的代码会生成NameErrors,因为像'BROCADE_1_0'这样的名称未在nfconftest.py中定义。你没有发布你正在运行的代码。也就是说,它看起来像'管理器'中的'OPERATIONS'字典是为了让你做这种事情。你看过吗? – BrenBarn

+0

因为我已经导入了'brcd_ncclient'来定义它们,''netconftest.py'关于'BROCADE_1_0'的运行过程中没有产生任何名称错误....'OPERATIONS'字典只是'ncclient.operations的一个奇特映射。 rpc.Method',这不是我真正想要改变的。我不确定您发布我正在运行的代码的意思。我已经发布了我正在运行的代码。 –

+0

导入'brcd_ncclient'不会使netconftest.py中的名称变为可用。你将不得不使用'from brcd_ncclient import *',或通过'brcd_ncclient.BROCADE_1_0'等来访问名称。 – BrenBarn

回答

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 

这让我几乎到了我想要的。我仍然需要编辑原始源代码来更改生成的消息标识符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 

幸得this recipe on ActiveState为终于在对着我诉说什么是我真正想做的事。我最初发布的代码我不认为在技术上是不正确的 - 如果我想要做的是分离我自己的ncclient并对其进行修改和/或维护它,这不是我想要做的,至少不是现在。

我会编辑我的问题标题,以更好地反映我最初的想法 - 如果其他人有更好或更清洁的想法,我完全开放。