Statistics
| Branch: | Tag: | Revision:

root / utils / proxy.py @ ca6bb3d5

History | View | Annotate | Download (9.9 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

    
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
        logger.info("[CACHE] hit! got device")
42
        if device:
43
            return device
44
        else:
45
            device = self.proccess_xml()
46
            if device.routing_options:
47
                cache.set("device", device, 3600)
48
                logger.info("[CACHE] miss, setting device")
49
                return device
50
            else:
51
                return False
52

    
53
class Applier(object):
54
    def __init__(self, route_objects = [], route_object=None, device=settings.NETCONF_DEVICE, username=settings.NETCONF_USER, password=settings.NETCONF_PASS):
55
        self.route_object = route_object
56
        self.route_objects = route_objects
57
        self.device = device
58
        self.username = username
59
        self.password = password
60
    
61
    def to_xml(self, operation=None):
62
        logger.info("Operation: %s"%operation)
63
        if self.route_object:
64
            logger.info("Generating XML config")
65
            route_obj = self.route_object
66
            device = np.Device()
67
            flow = np.Flow()
68
            route = np.Route()
69
            flow.routes.append(route)
70
            device.routing_options.append(flow)
71
            route.name = route_obj.name
72
            if operation == "delete":
73
                logger.info("Requesting a delete operation")
74
                route.operation = operation
75
                device = device.export(netconf_config=True)
76
                return ET.tostring(device)
77
            if route_obj.source:
78
                route.match['source'].append(route_obj.source)
79
            if route_obj.destination:
80
                route.match['destination'].append(route_obj.destination)
81
            if route_obj.protocol:
82
                route.match['protocol'].append(route_obj.protocol)
83
            try:
84
                if route_obj.port:
85
                    for port in route_obj.port.all():
86
                        route.match['port'].append(port.port)
87
            except:
88
                pass
89
            try:
90
                if route_obj.destinationport:
91
                    for port in route_obj.destinationport.all():
92
                        route.match['destination-port'].append(port.port)
93
            except:
94
                pass
95
            try:
96
                if route_obj.sourceport:
97
                    for port in route_obj.sourceport.all():
98
                        route.match['source-port'].append(port.port)
99
            except:
100
                pass
101
            if route_obj.icmpcode:
102
                route.match['icmp-code'].append(route_obj.icmpcode)
103
            if route_obj.icmptype:
104
                route.match['icmp-type'].append(route_obj.icmptype)
105
            if route_obj.tcpflag:
106
                route.match['tcp-flags'].append(route_obj.tcpflag)
107
            try:
108
                if route_obj.dscp:
109
                    for dscp in route_obj.dscp.all():
110
                        route.match['dscp'].append(dscp.dscp)
111
            except:
112
                pass
113
            if route_obj.fragmenttype:
114
                route.match['fragment'].append(route_obj.fragmenttype)
115
            for thenaction in route_obj.then.all():
116
                if thenaction.action_value:
117
                    route.then[thenaction.action] = thenaction.action_value
118
                else:
119
                    route.then[thenaction.action] = True
120
            if operation == "replace":
121
                logger.info("Requesting a replace operation")
122
                route.operation = operation
123
            device = device.export(netconf_config=True)
124
            return ET.tostring(device)
125
        else:
126
            return False
127

    
128
    def delete_routes(self):
129
        if self.route_objects:
130
            logger.info("Generating XML config")
131
            device = np.Device()
132
            flow = np.Flow()
133
            for route_object in self.route_objects:
134
                route_obj = route_object
135
                route = np.Route()
136
                flow.routes.append(route)
137
                route.name = route_obj.name
138
                route.operation = 'delete'
139
            device.routing_options.append(flow)
140
            device = device.export(netconf_config=True)
141
            return ET.tostring(device)
142
        else:
143
            return False    
144
    
145
    def apply(self, configuration = None, operation=None):
146
        reason = None
147
        if not configuration:
148
            configuration = self.to_xml(operation=operation)
149
        edit_is_successful = False
150
        commit_confirmed_is_successful = False
151
        commit_is_successful = False
152
        if configuration:
153
            with manager.connect(host=self.device, port=830, username=self.username, password=self.password) as m:
154
                assert(":candidate" in m.server_capabilities)
155
                with m.locked(target='candidate'):
156
                    m.discard_changes()
157
                    try:
158
                        edit_response = m.edit_config(target='candidate', config=configuration, test_option='test-then-set')
159
                        edit_is_successful, reason = is_successful(edit_response)
160
                        logger.info("Successfully edited @ %s" % self.device)
161
                        if not edit_is_successful:
162
                            raise Exception()
163
                    except Exception as e:
164
                        cause="Caught edit exception: %s %s" %(e,reason)
165
                        cause=cause.replace('\n', '')
166
                        logger.error(cause)
167
                        m.discard_changes()
168
                        return False, cause
169
                    if edit_is_successful:
170
                        try:
171
                            commit_confirmed_response = m.commit(confirmed=True, timeout=settings.COMMIT_CONFIRMED_TIMEOUT)
172
                            commit_confirmed_is_successful, reason = is_successful(commit_confirmed_response)
173
                                
174
                            if not commit_confirmed_is_successful:
175
                                raise Exception()
176
                            else:
177
                                logger.info("Successfully confirmed committed @ %s" % self.device)
178
                                if not settings.COMMIT:
179
                                    return True, "Successfully confirmed committed"
180
                        except Exception as e:
181
                            cause="Caught commit confirmed exception: %s %s" %(e,reason)
182
                            cause=cause.replace('\n', '')
183
                            logger.error(cause)
184
                            return False, cause
185
                        if settings.COMMIT:
186
                            if edit_is_successful and commit_confirmed_is_successful:
187
                                try:
188
                                    commit_response = m.commit(confirmed=False)
189
                                    commit_is_successful, reason = is_successful(commit_response)
190
                                    logger.info("Successfully committed @ %s" % self.device)
191
                                    newconfig = m.get_config(source='running', filter=('subtree',settings.ROUTES_FILTER)).data_xml
192
                                    retrieve = Retriever(xml=newconfig)
193
                                    logger.info("[CACHE] caching device configuration")
194
                                    cache.set("device", retrieve.proccess_xml(), 3600)
195
                                    
196
                                    if not commit_is_successful:
197
                                        raise Exception()
198
                                    else:
199
                                        logger.info("Successfully cached device configuration")
200
                                        return True, "Successfully committed"
201
                                except Exception as e:
202
                                    cause="Caught commit exception: %s %s" %(e,reason)
203
                                    cause=cause.replace('\n', '')
204
                                    logger.error(cause)
205
                                    return False, cause
206
        else:
207
            return False, "No configuration was supplied"
208
            
209
def is_successful(response):
210
    from StringIO import StringIO
211
    doc = parsexml_(StringIO(response))
212
    rootNode = doc.getroot()
213
    success_list = rootNode.xpath("//*[local-name()='ok']")
214
    if len(success_list)>0:
215
        return True, None
216
    else:
217
        reason_return = ''
218
        reason_list = rootNode.xpath("//*[local-name()='error-message']")
219
        for reason in reason_list:
220
            reason_return = "%s %s" %(reason_return, reason.text)  
221
        return False, reason_return
222
    
223
    
224
def parsexml_(*args, **kwargs):
225
    if 'parser' not in kwargs:
226
        kwargs['parser'] = ET.ETCompatXMLParser()
227
    doc = ET.parse(*args, **kwargs)
228
    return doc
229