Statistics
| Branch: | Tag: | Revision:

root / utils / proxy.py @ 7fac6521

History | View | Annotate | Download (10.5 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(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
def fod_unknown_host_cb(host, fingerprint):
26
    return True
27

    
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):
30
        self.device = device
31
        self.username = username
32
        self.password = password
33
        self.filter = filter
34
        self.xml = xml
35
        if route_name:
36
            self.filter = settings.ROUTE_FILTER%route_name
37
    
38
    def fetch_xml(self):
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
41
        return xmlconfig
42
    
43
    def proccess_xml(self):
44
        if self.xml:
45
            xmlconfig = self.xml
46
        else:
47
            xmlconfig = self.fetch_xml()
48
        parser = np.Parser()
49
        parser.confile = xmlconfig
50
        device = parser.export()
51
        return device    
52
    
53
    def fetch_device(self):
54
        device = cache.get("device")
55
        logger.info("[CACHE] hit! got device")
56
        if device:
57
            return device
58
        else:
59
            device = self.proccess_xml()
60
            if device.routing_options:
61
                cache.set("device", device, 3600)
62
                logger.info("[CACHE] miss, setting device")
63
                return device
64
            else:
65
                return False
66

    
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
71
        self.device = device
72
        self.username = username
73
        self.password = password
74
    
75
    def to_xml(self, operation=None):
76
        logger.info("Operation: %s"%operation)
77
        if self.route_object:
78
            logger.info("Generating XML config")
79
            route_obj = self.route_object
80
            device = np.Device()
81
            flow = np.Flow()
82
            route = np.Route()
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)
91
            if route_obj.source:
92
                route.match['source'].append(route_obj.source)
93
            if route_obj.destination:
94
                route.match['destination'].append(route_obj.destination)
95
            try:
96
                if route_obj.protocol:
97
                    for protocol in route_obj.protocol.all():
98
                        route.match['protocol'].append(protocol.protocol)
99
            except:
100
                pass
101
            try:
102
                if route_obj.port:
103
                    for port in route_obj.port.all():
104
                        route.match['port'].append(port.port)
105
            except:
106
                pass
107
            try:
108
                if route_obj.destinationport:
109
                    for port in route_obj.destinationport.all():
110
                        route.match['destination-port'].append(port.port)
111
            except:
112
                pass
113
            try:
114
                if route_obj.sourceport:
115
                    for port in route_obj.sourceport.all():
116
                        route.match['source-port'].append(port.port)
117
            except:
118
                pass
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)
125
            try:
126
                if route_obj.dscp:
127
                    for dscp in route_obj.dscp.all():
128
                        route.match['dscp'].append(dscp.dscp)
129
            except:
130
                pass
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
136
                else:
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)
143
        else:
144
            return False
145

    
146
    def delete_routes(self):
147
        if self.route_objects:
148
            logger.info("Generating XML config")
149
            device = np.Device()
150
            flow = np.Flow()
151
            for route_object in self.route_objects:
152
                route_obj = route_object
153
                route = np.Route()
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)
160
        else:
161
            return False    
162
    
163
    def apply(self, configuration = None, operation=None):
164
        reason = 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
170
        if configuration:
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'):
174
                    m.discard_changes()
175
                    try:
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:
180
                            raise Exception()
181
                    except Exception as e:
182
                        cause="Caught edit exception: %s %s" %(e,reason)
183
                        cause=cause.replace('\n', '')
184
                        logger.error(cause)
185
                        m.discard_changes()
186
                        return False, cause
187
                    if edit_is_successful:
188
                        try:
189
                            commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
190
                            commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
191
                                
192
                            if not commit_confirmed_is_successful:
193
                                raise Exception()
194
                            else:
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', '')
201
                            logger.error(cause)
202
                            return False, cause
203
                        if settings.COMMIT:
204
                            if edit_is_successful and commit_confirmed_is_successful:
205
                                try:
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)
213
                                    
214
                                    if not commit_is_successful:
215
                                        raise Exception()
216
                                    else:
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', '')
222
                                    logger.error(cause)
223
                                    return False, cause
224
        else:
225
            return False, "No configuration was supplied"
226
            
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:
233
        return True, None
234
    else:
235
        reason_return = ''
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
240
    
241
    
242
def parsexml_(*args, **kwargs):
243
    if 'parser' not in kwargs:
244
        kwargs['parser'] = ET.ETCompatXMLParser()
245
    doc = ET.parse(*args, **kwargs)
246
    return doc
247