root / utils / proxy.py @ 064ecc91
History | View | Annotate | Download (9.7 kB)
1 |
import nxpy as np |
---|---|
2 |
from ncclient import manager |
3 |
from ncclient.transport.errors import AuthenticationError, SSHError |
4 |
from lxml import etree as ET |
5 |
from django.conf import settings |
6 |
import logging |
7 |
from django.core.cache import cache |
8 |
|
9 |
FORMAT = '%(asctime)s %(levelname)s: %(message)s'
|
10 |
logging.basicConfig(format=FORMAT) |
11 |
logger = logging.getLogger(__name__) |
12 |
logger.setLevel(logging.DEBUG) |
13 |
|
14 |
class Retriever(object): |
15 |
def __init__(self, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS, filter=settings.ROUTES_FILTER, route_name=None, xml=None): |
16 |
self.device = device
|
17 |
self.username = username
|
18 |
self.password = password
|
19 |
self.filter = filter |
20 |
self.xml = xml
|
21 |
if route_name:
|
22 |
self.filter = settings.ROUTE_FILTER%route_name
|
23 |
|
24 |
def fetch_xml(self): |
25 |
with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m: |
26 |
xmlconfig = m.get_config(source='running', filter=('subtree',self.filter)).data_xml |
27 |
return xmlconfig
|
28 |
|
29 |
def proccess_xml(self): |
30 |
if self.xml: |
31 |
xmlconfig = self.xml
|
32 |
else:
|
33 |
xmlconfig = self.fetch_xml()
|
34 |
parser = np.Parser() |
35 |
parser.confile = xmlconfig |
36 |
device = parser.export() |
37 |
return device
|
38 |
|
39 |
def fetch_device(self): |
40 |
device = cache.get("device")
|
41 |
if device:
|
42 |
return device
|
43 |
else:
|
44 |
device = self.proccess_xml()
|
45 |
if device.routing_options:
|
46 |
cache.set("device", device, 600) |
47 |
return device
|
48 |
else:
|
49 |
return False |
50 |
|
51 |
class Applier(object): |
52 |
def __init__(self, route_objects = [], route_object=None, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS): |
53 |
self.route_object = route_object
|
54 |
self.route_objects = route_objects
|
55 |
self.device = device
|
56 |
self.username = username
|
57 |
self.password = password
|
58 |
|
59 |
def to_xml(self): |
60 |
if self.route_object: |
61 |
logger.info("Generating XML config")
|
62 |
route_obj = self.route_object
|
63 |
device = np.Device() |
64 |
flow = np.Flow() |
65 |
route = np.Route() |
66 |
flow.routes.append(route) |
67 |
device.routing_options.append(flow) |
68 |
route.name = route_obj.name |
69 |
match = route_obj.match |
70 |
if match.matchSource:
|
71 |
route.match['source'].append(match.matchSource.address)
|
72 |
if match.matchDestination:
|
73 |
route.match['destination'].append(match.matchDestination.address)
|
74 |
if match.matchprotocol:
|
75 |
route.match['protocol'].append(match.matchprotocol.protocol)
|
76 |
if match.matchport:
|
77 |
for port in match.matchport.all(): |
78 |
route.match['port'].append(port.port)
|
79 |
if match.matchDestinationPort:
|
80 |
for port in match.matchDestinationPort.all(): |
81 |
route.match['destination-port'].append(port.port)
|
82 |
if match.matchSourcePort:
|
83 |
for port in match.matchSourcePort.all(): |
84 |
route.match['source-port'].append(port.port)
|
85 |
if match.matchicmpcode:
|
86 |
route.match['icmp-code'].append(match.matchicmpcode.icmp_code)
|
87 |
if match.matchicmptype:
|
88 |
route.match['icmp-type'].append(match.matchicmptype.icmp_type)
|
89 |
if match.matchTcpFlag:
|
90 |
route.match['tcp-flags'].append(match.matchTcpFlag.tcp_flags)
|
91 |
if match.matchdscp:
|
92 |
for dscp in match.matchdscp.all(): |
93 |
route.match['dscp'].append(dscp.dscp)
|
94 |
if match.matchfragmenttype:
|
95 |
route.match['fragment'].append(match.matchfragmenttype.fragmenttype)
|
96 |
then = route_obj.then |
97 |
for thenaction in then.thenaction.all(): |
98 |
if thenaction.action_value:
|
99 |
route.then[thenaction.action] = thenaction.action_value |
100 |
else:
|
101 |
route.then[thenaction.action] = True
|
102 |
device = device.export(netconf_config=True)
|
103 |
return ET.tostring(device)
|
104 |
else:
|
105 |
return False |
106 |
|
107 |
def delete_route(self): |
108 |
if self.route_object: |
109 |
logger.info("Generating XML config")
|
110 |
route_obj = self.route_object
|
111 |
device = np.Device() |
112 |
flow = np.Flow() |
113 |
route = np.Route() |
114 |
flow.routes.append(route) |
115 |
device.routing_options.append(flow) |
116 |
route.name = route_obj.name |
117 |
route.operation = 'delete'
|
118 |
device = device.export(netconf_config=True)
|
119 |
return ET.tostring(device)
|
120 |
else:
|
121 |
return False |
122 |
|
123 |
def delete_routes(self): |
124 |
if self.route_objects: |
125 |
logger.info("Generating XML config")
|
126 |
device = np.Device() |
127 |
flow = np.Flow() |
128 |
for route_object in self.route_objects: |
129 |
route_obj = route_object |
130 |
route = np.Route() |
131 |
flow.routes.append(route) |
132 |
route.name = route_obj.name |
133 |
route.operation = 'delete'
|
134 |
device.routing_options.append(flow) |
135 |
device = device.export(netconf_config=True)
|
136 |
return ET.tostring(device)
|
137 |
else:
|
138 |
return False |
139 |
|
140 |
def apply(self, configuration = None): |
141 |
reason = None
|
142 |
if not configuration: |
143 |
configuration = self.to_xml()
|
144 |
edit_is_successful = False
|
145 |
commit_confirmed_is_successful = False
|
146 |
commit_is_successful = False
|
147 |
if configuration:
|
148 |
with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m: |
149 |
assert(":candidate" in m.server_capabilities) |
150 |
with m.locked(target='candidate'): |
151 |
m.discard_changes() |
152 |
try:
|
153 |
edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set') |
154 |
edit_is_successful, reason = is_successful(edit_response) |
155 |
logger.info("Successfully edited @ %s" % self.device) |
156 |
if not edit_is_successful: |
157 |
raise Exception() |
158 |
except Exception as e: |
159 |
cause="Caught edit exception: %s %s" %(e,reason)
|
160 |
cause=cause.replace('\n', '') |
161 |
logger.error(cause) |
162 |
m.discard_changes() |
163 |
return False, cause |
164 |
if edit_is_successful:
|
165 |
try:
|
166 |
commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
|
167 |
commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response) |
168 |
|
169 |
if not commit_confirmed_is_successful: |
170 |
raise Exception() |
171 |
else:
|
172 |
logger.info("Successfully confirmed committed @ %s" % self.device) |
173 |
if not settings.COMMIT: |
174 |
return True, "Successfully confirmed committed" |
175 |
except Exception as e: |
176 |
cause="Caught commit confirmed exception: %s %s" %(e,reason)
|
177 |
cause=cause.replace('\n', '') |
178 |
logger.error(cause) |
179 |
return False, cause |
180 |
if settings.COMMIT:
|
181 |
if edit_is_successful and commit_confirmed_is_successful: |
182 |
try:
|
183 |
commit_response = m.commit(confirmed=False)
|
184 |
commit_is_successful, reason = is_successful(commit_response) |
185 |
logger.info("Successfully committed @ %s" % self.device) |
186 |
newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml |
187 |
retrieve = Retriever(xml=newconfig) |
188 |
cache.set("device", retrieve.proccess_xml(), 600) |
189 |
|
190 |
if not commit_is_successful: |
191 |
raise Exception() |
192 |
else:
|
193 |
logger.info("Successfully cached device configuration")
|
194 |
return True, "Successfully committed" |
195 |
except Exception as e: |
196 |
cause="Caught commit exception: %s %s" %(e,reason)
|
197 |
cause=cause.replace('\n', '') |
198 |
logger.error(cause) |
199 |
return False, cause |
200 |
else:
|
201 |
return False, "No configuration was supplied" |
202 |
|
203 |
def is_successful(response): |
204 |
from StringIO import StringIO |
205 |
doc = parsexml_(StringIO(response)) |
206 |
rootNode = doc.getroot() |
207 |
success_list = rootNode.xpath("//*[local-name()='ok']")
|
208 |
if len(success_list)>0: |
209 |
return True, None |
210 |
else:
|
211 |
reason_return = ''
|
212 |
reason_list = rootNode.xpath("//*[local-name()='error-message']")
|
213 |
for reason in reason_list: |
214 |
reason_return = "%s %s" %(reason_return, reason.text)
|
215 |
return False, reason_return |
216 |
|
217 |
|
218 |
def parsexml_(*args, **kwargs): |
219 |
if 'parser' not in kwargs: |
220 |
kwargs['parser'] = ET.ETCompatXMLParser()
|
221 |
doc = ET.parse(*args, **kwargs) |
222 |
return doc
|
223 |
|