blob: 4d921dda699dfbb7a077cb1be55522a3cd50c909 [file] [log] [blame]
Khen Nursimulua7b842a2016-12-03 23:28:42 -05001#!/usr/bin/env python
2#
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -05003# Copyright 2017 the original author or authors.
Khen Nursimulua7b842a2016-12-03 23:28:42 -05004#
5# Code adapted from https://github.com/choppsv1/netconf
6#
7# Licensed under the Apache License, Version 2.0 (the "License");
8# you may not use this file except in compliance with the License.
9# You may obtain a copy of the License at
10#
11# http://www.apache.org/licenses/LICENSE-2.0
12#
13# Unless required by applicable law or agreed to in writing, software
14# distributed under the License is distributed on an "AS IS" BASIS,
15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16# See the License for the specific language governing permissions and
17# limitations under the License.
18#
Khen Nursimulua4972742016-12-23 17:15:20 -050019import structlog
20from lxml import etree
21import netconf.nc_common.error as ncerror
22
23log = structlog.get_logger()
24
Khen Nursimulua7b842a2016-12-03 23:28:42 -050025
26class RpcResponse():
27 def __init__(self):
28 self.is_error = False
29 # if there is an error then the reply_node will contains an Error
30 # object
31 self.reply_node = None
Khen Nursimulua4972742016-12-23 17:15:20 -050032 self.close_session = False
33
34 def build_xml_response(self, request, voltha_response):
35 if request is None:
36 return
37 voltha_xml_string = etree.tostring(voltha_response)
38
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -050039 # Remove the leading and trailing <yang> tags
Khen Nursimulua4972742016-12-23 17:15:20 -050040 if voltha_xml_string.startswith('<yang>'):
41 voltha_xml_string = voltha_xml_string[len('<yang>'):]
42 if voltha_xml_string.endswith('</yang>'):
43 voltha_xml_string = voltha_xml_string[:-len('</yang>')]
Khen Nursimuluc7991dd2017-01-05 17:05:48 -050044 # Empty response
45 elif voltha_xml_string.startswith('<yang/>'):
Khen Nursimulub4e71472017-01-06 18:05:47 -050046 voltha_xml_string = ''
Khen Nursimulua4972742016-12-23 17:15:20 -050047
48 # Create the xml body as
49 if request.has_key('subclass'):
50 body = ''.join([
51 '<data>',
52 '<',
53 request['class'],
Khen Nursimuluc9ef7c12017-01-04 20:40:53 -050054 ' xmlns="',
55 request['namespace'],
56 '">',
Khen Nursimulua4972742016-12-23 17:15:20 -050057 '<',
58 request['subclass'],
59 '>',
60 voltha_xml_string,
61 '</',
62 request['subclass'],
63 '>',
64 '</',
65 request['class'],
66 '>',
67 '</data>'
68 ])
69 else:
70 body = ''.join([
71 '<data>',
72 '<',
73 request['class'],
74 ' xmlns="urn:opencord:params:xml:ns:voltha:ietf-voltha">',
75 voltha_xml_string,
76 '</',
77 request['class'],
78 '>',
79 '</data>'
80 ])
81
82 return etree.fromstring(body)
83
84 def add_node(self, new_node, tree):
85 if new_node.tag == 'ignore':
86 # We want only sub-elements
87 for elem in list(new_node):
88 tree.append(elem)
89 else:
90 tree.append(new_node)
91
92 def copy_basic_element(self, elm):
93 new_elem = etree.Element(elm.tag)
94 new_elem.text = elm.text
95 return new_elem
96
97 def process_inline_option(self, elem):
98 inline_option = False
99 inline_node_name = None
100 for elm in list(elem):
101 if elm.tag == 'yang_field_option':
102 inline_option = True
103 if elm.tag == 'name':
104 inline_node_name = elm.text
105 if not inline_option:
106 new_elem = etree.Element(elem.tag)
107 return new_elem, elem
108
109 # look for the node with the inline_node_name
110 for elm in list(elem):
111 if elm.tag == inline_node_name:
112 new_elem = etree.Element('ignore')
113 return new_elem, elm
114
115 def process_element(self, elem):
116 attrib = elem.get('type')
117 if (attrib == 'list'):
118 if list(elem) is None:
119 return self.copy_basic_element(elem)
Khen Nursimulub4e71472017-01-06 18:05:47 -0500120 if elem.tag == 'items':
121 new_elem = etree.Element('items')
122 else:
123 new_elem = etree.Element('ignore')
Khen Nursimulua4972742016-12-23 17:15:20 -0500124 for elm in list(elem):
125 elm.tag = elem.tag
126 if elm.get('type') in ['list', 'dict']:
127 self.add_node(self.process_element(elm), new_elem)
128 else:
129 new_elem.append(self.copy_basic_element(elm))
130 return new_elem
131 elif (attrib == 'dict'):
132 # Empty case
133 if list(elem) is None:
134 return self.copy_basic_element(elem)
135
136 # Process field option.
137 new_elem, elem = self.process_inline_option(elem)
138
139 for elm in list(elem):
140 if elm.get('type') in ['list', 'dict']:
141 self.add_node(self.process_element(elm), new_elem)
142 else:
143 new_elem.append(self.copy_basic_element(elm))
144 return new_elem
145 else:
146 return self.copy_basic_element(elem)
147
Khen Nursimulub4e71472017-01-06 18:05:47 -0500148 def to_yang_xml(self, from_xml, request):
Khen Nursimulua4972742016-12-23 17:15:20 -0500149 # Parse from_xml as follows:
150 # 1. Any element having a list attribute shoud have each item move 1 level
151 # up and retag using the parent tag
152 # 2. Any element having a dict attribute and has a <yang_field_option>
153 # sub-element should have all it's items move to teh parent level
154 top = etree.Element('yang')
155 elms = list(from_xml)
156
157 # special case the xml contain a list type
158 if len(elms) == 1:
159 item = elms[0]
Khen Nursimulub4e71472017-01-06 18:05:47 -0500160 # TODO: Address name 'items' clash when a list name is actually
161 # 'items'.
Khen Nursimulua4972742016-12-23 17:15:20 -0500162 if item.get('type') == 'list':
Khen Nursimulub4e71472017-01-06 18:05:47 -0500163 if request.has_key('subclass'):
164 item.tag = request['subclass']
165 # remove the subclass element in request to avoid duplicate tag
166 del request['subclass']
167 else:
168 item.tag = 'ignore'
169 # item.tag = 'ignore'
Khen Nursimulua4972742016-12-23 17:15:20 -0500170 self.add_node(self.process_element(item), top)
171 return top
172
173 # Process normally for all other cases
174 for elm in elms:
175 self.add_node(self.process_element(elm), top)
176
177 return top
178
179 def build_yang_response(self, root, request):
180 try:
Khen Nursimulub4e71472017-01-06 18:05:47 -0500181 yang_xml = self.to_yang_xml(root, request)
Khen Nursimulua4972742016-12-23 17:15:20 -0500182 log.info('yang-xml', yang_xml=etree.tounicode(yang_xml,
183 pretty_print=True))
184 return self.build_xml_response(request, yang_xml)
185 except Exception as e:
186 self.rpc_response.is_error = True
187 self.rpc_response.node = ncerror.BadMsg(request)
188 return