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