Changed logic to all parts. Dev db is incorporated
[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
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             if route_obj.source:
70                 route.match['source'].append(route_obj.source)
71             if route_obj.destination:
72                 route.match['destination'].append(route_obj.destination)
73             if route_obj.protocol:
74                 route.match['protocol'].append(route_obj.protocol)
75             try:
76                 if route_obj.port:
77                     for port in route_obj.port.all():
78                         route.match['port'].append(port.port)
79             except:
80                 pass
81             try:
82                 if route_obj.destinationport:
83                     for port in route_obj.destinationport.all():
84                         route.match['destination-port'].append(port.port)
85             except:
86                 pass
87             try:
88                 if route_obj.sourceport:
89                     for port in route_obj.sourceport.all():
90                         route.match['source-port'].append(port.port)
91             except:
92                 pass
93             if route_obj.icmpcode:
94                 route.match['icmp-code'].append(route_obj.icmpcode)
95             if route_obj.icmptype:
96                 route.match['icmp-type'].append(route_obj.icmptype)
97             if route_obj.tcpflag:
98                 route.match['tcp-flags'].append(route_obj.tcpflag)
99             try:
100                 if route_obj.dscp:
101                     for dscp in route_obj.dscp.all():
102                         route.match['dscp'].append(dscp.dscp)
103             except:
104                 pass
105             if route_obj.fragmenttype:
106                 route.match['fragment'].append(route_obj.fragmenttype)
107             for thenaction in route_obj.then.all():
108                 if thenaction.action_value:
109                     route.then[thenaction.action] = thenaction.action_value
110                 else:
111                     route.then[thenaction.action] = True
112             device = device.export(netconf_config=True)
113             return ET.tostring(device)
114         else:
115             return False
116     
117     def delete_route(self):
118         if self.route_object:
119             logger.info("Generating XML config")
120             route_obj = self.route_object
121             device = np.Device()
122             flow = np.Flow()
123             route = np.Route()
124             flow.routes.append(route)
125             device.routing_options.append(flow)
126             route.name = route_obj.name
127             route.operation = 'delete'
128             device = device.export(netconf_config=True)
129             return ET.tostring(device)
130         else:
131             return False
132
133     def delete_routes(self):
134         if self.route_objects:
135             logger.info("Generating XML config")
136             device = np.Device()
137             flow = np.Flow()
138             for route_object in self.route_objects:
139                 route_obj = route_object
140                 route = np.Route()
141                 flow.routes.append(route)
142                 route.name = route_obj.name
143                 route.operation = 'delete'
144             device.routing_options.append(flow)
145             device = device.export(netconf_config=True)
146             return ET.tostring(device)
147         else:
148             return False    
149     
150     def apply(self, configuration = None):
151         reason = None
152         if not configuration:
153             configuration = self.to_xml()
154         edit_is_successful = False
155         commit_confirmed_is_successful = False
156         commit_is_successful = False
157         if configuration:
158             with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m:
159                 assert(":candidate" in m.server_capabilities)
160                 with m.locked(target='candidate'):
161                     m.discard_changes()
162                     try:
163                         edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set')
164                         edit_is_successful, reason = is_successful(edit_response)
165                         logger.info("Successfully edited @ %s" % self.device)
166                         if not edit_is_successful:
167                             raise Exception()
168                     except Exception as e:
169                         cause="Caught edit exception: %s %s" %(e,reason)
170                         cause=cause.replace('\n', '')
171                         logger.error(cause)
172                         m.discard_changes()
173                         return False, cause
174                     if edit_is_successful:
175                         try:
176                             commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
177                             commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
178                                 
179                             if not commit_confirmed_is_successful:
180                                 raise Exception()
181                             else:
182                                 logger.info("Successfully confirmed committed @ %s" % self.device)
183                                 if not settings.COMMIT:
184                                     return True, "Successfully confirmed committed"
185                         except Exception as e:
186                             cause="Caught commit confirmed exception: %s %s" %(e,reason)
187                             cause=cause.replace('\n', '')
188                             logger.error(cause)
189                             return False, cause
190                         if settings.COMMIT:
191                             if edit_is_successful and commit_confirmed_is_successful:
192                                 try:
193                                     commit_response = m.commit(confirmed=False)
194                                     commit_is_successful, reason = is_successful(commit_response)
195                                     logger.info("Successfully committed @ %s" % self.device)
196                                     newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml
197                                     retrieve = Retriever(xml=newconfig)
198                                     cache.set("device", retrieve.proccess_xml(), 600)
199                                     
200                                     if not commit_is_successful:
201                                         raise Exception()
202                                     else:
203                                         logger.info("Successfully cached device configuration")
204                                         return True, "Successfully committed"
205                                 except Exception as e:
206                                     cause="Caught commit exception: %s %s" %(e,reason)
207                                     cause=cause.replace('\n', '')
208                                     logger.error(cause)
209                                     return False, cause
210         else:
211             return False, "No configuration was supplied"
212             
213 def is_successful(response):
214     from StringIO import StringIO
215     doc = parsexml_(StringIO(response))
216     rootNode = doc.getroot()
217     success_list = rootNode.xpath("//*[local-name()='ok']")
218     if len(success_list)>0:
219         return True, None
220     else:
221         reason_return = ''
222         reason_list = rootNode.xpath("//*[local-name()='error-message']")
223         for reason in reason_list:
224             reason_return = "%s %s" %(reason_return, reason.text)  
225         return False, reason_return
226     
227     
228 def parsexml_(*args, **kwargs):
229     if 'parser' not in kwargs:
230         kwargs['parser'] = ET.ETCompatXMLParser()
231     doc = ET.parse(*args, **kwargs)
232     return doc
233