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
7 from django.core.cache import cache
13 LOG_FILENAME = os.path.join(settings.LOG_FILE_LOCATION, 'celery_jobs.log')
15 #FORMAT = '%(asctime)s %(levelname)s: %(message)s'
16 #logging.basicConfig(format=FORMAT)
17 formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
19 logger = logging.getLogger(__name__)
20 logger.setLevel(logging.DEBUG)
21 handler = logging.FileHandler(LOG_FILENAME)
22 handler.setFormatter(formatter)
23 logger.addHandler(handler)
25 def fod_unknown_host_cb(host, fingerprint):
28 class Retriever(object):
29 def __init__(self, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS, filter=settings.ROUTES_FILTER, route_name=None, xml=None):
31 self.username = username
32 self.password = password
36 self.filter = settings.ROUTE_FILTER%route_name
39 with manager.connect(host=self.device, port=830, username=self.username, password=self.password, unknown_host_cb=fod_unknown_host_cb) as m:
40 xmlconfig = m.get_config(source='running', filter=('subtree',self.filter)).data_xml
43 def proccess_xml(self):
47 xmlconfig = self.fetch_xml()
49 parser.confile = xmlconfig
50 device = parser.export()
53 def fetch_device(self):
54 device = cache.get("device")
55 logger.info("[CACHE] hit! got device")
59 device = self.proccess_xml()
60 if device.routing_options:
61 cache.set("device", device, 3600)
62 logger.info("[CACHE] miss, setting device")
67 class Applier(object):
68 def __init__(self, route_objects = [], route_object=None, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS):
69 self.route_object = route_object
70 self.route_objects = route_objects
72 self.username = username
73 self.password = password
75 def to_xml(self, operation=None):
76 logger.info("Operation: %s"%operation)
78 logger.info("Generating XML config")
79 route_obj = self.route_object
83 flow.routes.append(route)
84 device.routing_options.append(flow)
85 route.name = route_obj.name
86 if operation == "delete":
87 logger.info("Requesting a delete operation")
88 route.operation = operation
89 device = device.export(netconf_config=True)
90 return ET.tostring(device)
92 route.match['source'].append(route_obj.source)
93 if route_obj.destination:
94 route.match['destination'].append(route_obj.destination)
96 if route_obj.protocol:
97 for protocol in route_obj.protocol.all():
98 route.match['protocol'].append(protocol.protocol)
103 for port in route_obj.port.all():
104 route.match['port'].append(port.port)
108 if route_obj.destinationport:
109 for port in route_obj.destinationport.all():
110 route.match['destination-port'].append(port.port)
114 if route_obj.sourceport:
115 for port in route_obj.sourceport.all():
116 route.match['source-port'].append(port.port)
119 if route_obj.icmpcode:
120 route.match['icmp-code'].append(route_obj.icmpcode)
121 if route_obj.icmptype:
122 route.match['icmp-type'].append(route_obj.icmptype)
123 if route_obj.tcpflag:
124 route.match['tcp-flags'].append(route_obj.tcpflag)
127 for dscp in route_obj.dscp.all():
128 route.match['dscp'].append(dscp.dscp)
131 if route_obj.fragmenttype:
132 route.match['fragment'].append(route_obj.fragmenttype)
133 for thenaction in route_obj.then.all():
134 if thenaction.action_value:
135 route.then[thenaction.action] = thenaction.action_value
137 route.then[thenaction.action] = True
138 if operation == "replace":
139 logger.info("Requesting a replace operation")
140 route.operation = operation
141 device = device.export(netconf_config=True)
142 return ET.tostring(device)
146 def delete_routes(self):
147 if self.route_objects:
148 logger.info("Generating XML config")
151 for route_object in self.route_objects:
152 route_obj = route_object
154 flow.routes.append(route)
155 route.name = route_obj.name
156 route.operation = 'delete'
157 device.routing_options.append(flow)
158 device = device.export(netconf_config=True)
159 return ET.tostring(device)
163 def apply(self, configuration = None, operation=None):
165 if not configuration:
166 configuration = self.to_xml(operation=operation)
167 edit_is_successful = False
168 commit_confirmed_is_successful = False
169 commit_is_successful = False
171 with manager.connect(host=self.device, port=830, username=self.username, password=self.password, unknown_host_cb=fod_unknown_host_cb) as m:
172 assert(":candidate" in m.server_capabilities)
173 with m.locked(target='candidate'):
176 edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set')
177 edit_is_successful, reason = is_successful(edit_response)
178 logger.info("Successfully edited @ %s" % self.device)
179 if not edit_is_successful:
181 except Exception as e:
182 cause="Caught edit exception: %s %s" %(e,reason)
183 cause=cause.replace('\n', '')
187 if edit_is_successful:
189 commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
190 commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
192 if not commit_confirmed_is_successful:
195 logger.info("Successfully confirmed committed @ %s" % self.device)
196 if not settings.COMMIT:
197 return True, "Successfully confirmed committed"
198 except Exception as e:
199 cause="Caught commit confirmed exception: %s %s" %(e,reason)
200 cause=cause.replace('\n', '')
204 if edit_is_successful and commit_confirmed_is_successful:
206 commit_response = m.commit(confirmed=False)
207 commit_is_successful, reason = is_successful(commit_response)
208 logger.info("Successfully committed @ %s" % self.device)
209 newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml
210 retrieve = Retriever(xml=newconfig)
211 logger.info("[CACHE] caching device configuration")
212 cache.set("device", retrieve.proccess_xml(), 3600)
214 if not commit_is_successful:
217 logger.info("Successfully cached device configuration")
218 return True, "Successfully committed"
219 except Exception as e:
220 cause="Caught commit exception: %s %s" %(e,reason)
221 cause=cause.replace('\n', '')
225 return False, "No configuration was supplied"
227 def is_successful(response):
228 from StringIO import StringIO
229 doc = parsexml_(StringIO(response))
230 rootNode = doc.getroot()
231 success_list = rootNode.xpath("//*[local-name()='ok']")
232 if len(success_list)>0:
236 reason_list = rootNode.xpath("//*[local-name()='error-message']")
237 for reason in reason_list:
238 reason_return = "%s %s" %(reason_return, reason.text)
239 return False, reason_return
242 def parsexml_(*args, **kwargs):
243 if 'parser' not in kwargs:
244 kwargs['parser'] = ET.ETCompatXMLParser()
245 doc = ET.parse(*args, **kwargs)