2013-01-05 9 views
6

[EDIT: używam Python 2.7.3]Jak przesłonić elementy w pakiecie w czasie wykonywania?

Jestem inżynierem sieci przez handel, a ja już hacking na ncclient (wersja na stronie jest stary, a this była wersja I pracowaliśmy nad), aby działał z implementacją NETCONF przez Brocade. Jest kilka poprawek, które musiałem wprowadzić, aby uruchomić go z naszym sprzętem Brocade, ale musiałem oderwać paczkę i zrobić poprawki do samego źródła. To nie było dla mnie "czyste", więc zdecydowałem, że chcę spróbować zrobić to "we właściwy sposób" i zastąpić kilka rzeczy, które istnieją w pakiecie *; trzy rzeczy, konkretnie:

  1. A „metoda statyczna” o nazwie build(), która należy do klasy HelloHandler, która sama jest podklasą SessionListener
  2. z „._id” atrybutem klasy RPC (oryginalna implementacja używane uuid, a skrzynie Brocade nie bardzo to lubiły, więc w moich oryginalnych poprawkach zmieniłem to na statyczną wartość, która nigdy się nie zmieniła).
  3. mały uszczypnąć do funkcji util który buduje filtr XML atrybuty

tej pory mam ten kod w pliku 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 

a następnie w moim pliku netconftest.py mam:

#!/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' 

Po uruchomieniu pliku netconftest.py pojawia się błąd przekroczenia limitu czasu, ponieważ w pliku dziennika ncclient.log I można zauważyć, że moje definicje podklasy (a mianowicie ta, która zmienia XML dla wymiany hello - staticmethod build) są ignorowane, a pole Brocade nie wie, jak interpretować XML, który generuje oryginalny sposób ncclient HelloHandler.build() **. Mogę także zobaczyć w wygenerowanym pliku dziennika, że ​​inne rzeczy, które próbuję przesłonić, również są ignorowane, takie jak id komunikatu (wartość statyczna 1), a także filtry XML.

Tak więc jestem w pewnym sensie stratą. Z moich badań znalazłem this blog post/module i wydawałoby się, że robię dokładnie to, co chcę, ale naprawdę chciałbym móc zrozumieć, co robię źle, robiąc to ręcznie, zamiast używać modułu, który ktoś ma już napisany jako wymówka, aby nie musieć tego samodzielnie wymyślać.

* Czy ktoś może mi wyjaśnić, czy jest to "łatanie małpy", czy w rzeczywistości jest złe? Widziałem w moich badaniach, że łatanie małp nie jest pożądane, ale this answer i this answer wprowadzają mnie w zakłopotanie. Dla mnie moje pragnienie obejścia tych bitów uniemożliwiłoby mi utrzymanie całego widelca mojego własnego klienta.

** Aby dać trochę więcej kontekstu, ten XML, który ncclient.transport.session.HelloHandler.build() generuje domyślnie okno Brocade wydaje się nie podoba:

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

Celem mojego zamienione na build() metody jest to, aby włączyć powyżej XML do tego (co Brocade lubi:

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

Twój kod wygeneruje NameErrors, ponieważ nazwy takie jak 'BROCADE_1_0' nie są zdefiniowane w pliku nfconftest.py. Nie publikujesz uruchomionego kodu. To powiedziawszy, wygląda na to, że dyktando 'OPERATIONS' w' managerze 'ma pozwolić ci robić takie rzeczy. Czy spojrzałeś na to? – BrenBarn

+0

Żadne błędy nazw nie są generowane podczas uruchamiania 'netconftest.py' na temat' BROCADE_1_0', ponieważ zaimportowałem 'brcd_ncclient', który je definiuje .... dyktando' OPERATIONS' jest po prostu fantazyjną mapą do 'ncclient.operations. rpc.Method' i to nie jest to, co naprawdę chcę zmienić. Nie jestem pewien, co masz na myśli, mówiąc o umieszczaniu kodu, który używam. Opublikowalem kod, z którego korzystam. –

+0

Importowanie 'brcd_ncclient' nie spowoduje udostępnienia nazw w netconftest.py. Musiałbyś użyć 'z brcd_ncclient import *' lub uzyskać dostęp do nazw poprzez 'brcd_ncclient.BROCADE_1_0', itd. – BrenBarn

Odpowiedz

2

tak więc okazuje się, że „meta info” nie powinno być tak szybko usunięty, bo znowu, trudno jest znaleźć odpowiedź na to, co jestem po kiedy nie w pełni rozumiem, czego chcę zapytać.Naprawdę chciałem zastąpić rzeczy w pakiecie w środowisku wykonawczym.

Oto co zmieniłem brcd_ncclient.py do (komentarze usunięte dla zwięzłość):

#!/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 

A potem w 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 

To dostaje mi prawie do miejsca, gdzie chcę być. Nadal muszę edytować oryginalny kod źródłowy, aby zmienić generowanie identyfikatora wiadomości za pomocą uuid1().urn, ponieważ nie zorientowałem się lub nie rozumiem, jak zmienić atrybuty obiektu, zanim stanie się on w czasie wykonywania (problem z kurczakiem/jajem?) ; Oto kod naruszającym przepisy w 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 

zasługa this recipe on ActiveState na końcu cluing mi na tym, co naprawdę chciałem zrobić. Kod, który pierwotnie zamieściłem, nie wydaje mi się technicznie niepoprawny - jeśli chciałem to zrobić, to otworzyć mój własny klient i wprowadzić w nim zmiany i/lub go utrzymać, co nie było tym, co chciałem w ogóle zrobić, przynajmniej nie teraz.

Będę edytować mój tytuł pytania, aby lepiej odzwierciedlał to, co pierwotnie chciałem - jeśli inni ludzie mają lepsze lub czystsze pomysły, jestem całkowicie otwarty.

Powiązane problemy