Code formatting
[flowspy] / utils / proxy.py
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(settings.LOG_FILE_LOCATION, '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