4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
30 gi
.require_version('RwYang', '1.0')
31 gi
.require_version('RwVnfdYang', '1.0')
32 gi
.require_version('RwNsdYang', '1.0')
33 from gi
.repository
import (
39 logging
.basicConfig(level
=logging
.WARNING
)
40 logger
= logging
.getLogger("openmano2rift.py")
43 class UnknownVNFError(Exception):
47 class DescriptorFileWriter(object):
48 def __init__(self
, module_list
, output_dir
, output_format
):
49 self
._model
= RwYang
.Model
.create_libncx()
50 for module
in module_list
:
51 self
._model
.load_module(module
)
53 self
._output
_dir
= output_dir
54 self
._output
_format
= output_format
56 def _write_file(self
, file_name
, output
):
57 file_path
= os
.path
.join(self
._output
_dir
, file_name
)
58 dir_path
= os
.path
.dirname(file_path
)
59 if not os
.path
.exists(dir_path
):
62 with
open(file_path
, "w") as hdl
:
65 logger
.info("Wrote descriptor to %s", file_path
)
67 def _write_json(self
, descriptor
, subdir
):
69 '%s.json' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
70 descriptor
.descriptor
.to_json(self
._model
)
73 def _write_xml(self
, descriptor
, subdir
):
75 '%s.xml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
76 descriptor
.descriptor
.to_xml_v2(self
._model
, pretty_print
=True)
79 def _write_yaml(self
, descriptor
, subdir
):
81 '%s.yaml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
82 yaml
.dump(descriptor
.descriptor
.as_dict()),
85 def write_descriptor(self
, descriptor
, subdir
=""):
86 if self
._output
_format
== 'json':
87 self
._write
_json
(descriptor
, subdir
=subdir
)
89 elif self
._output
_format
== 'xml':
90 self
._write
_xml
(descriptor
, subdir
=subdir
)
92 elif self
._output
_format
== 'yaml':
93 self
._write
_yaml
(descriptor
, subdir
=subdir
)
96 class RiftManoDescriptor(object):
97 def __init__(self
, openmano
=None):
98 self
.openmano
= openmano
99 self
.descriptor
= None
102 class RiftNS(RiftManoDescriptor
):
103 def __init__(self
, openmano
=None):
104 super().__init
__(openmano
)
105 self
.nsd_catalog
= None
109 def get_vnfd_id(self
, vnf_list
, vnf_name
):
111 if vnf
.name
== vnf_name
:
114 # Didn't find the vnf just return the vnf_name
117 def openmano2rift(self
, vnf_list
):
118 self
.descriptor
= RwNsdYang
.YangData_Nsd_NsdCatalog()
119 openmano_nsd
= self
.openmano
.dictionary
120 self
.name
= openmano_nsd
['name']
121 nsd
= self
.descriptor
.nsd
.add()
122 nsd
.id = str(uuid
.uuid1())
124 nsd
.short_name
= self
.name
125 nsd
.description
= openmano_nsd
['description']
127 nodes
= openmano_nsd
['topology']['nodes']
128 connections
= openmano_nsd
['topology']['connections']
130 def create_consituent_vnfds():
131 vnf_member_index_dict
= {}
133 vnfd_idx_gen
= itertools
.count(1)
136 if node
['type'] != 'VNF':
139 vnfd_idx
= next(vnfd_idx_gen
)
140 constituent_vnfd
= nsd
.constituent_vnfd
.add()
141 constituent_vnfd
.member_vnf_index
= vnfd_idx
142 constituent_vnfd
.vnfd_id_ref
= self
.get_vnfd_id(vnf_list
, node
['VNF model'])
143 vnf_member_index_dict
[key
] = vnfd_idx
145 return vnf_member_index_dict
147 def create_connections(vnf_member_index_dict
):
148 keys
= connections
.keys()
150 # TODO: Need clarification from TEF
151 # skip the mgmtnet in OpenMANO descriptor
154 conn
= connections
[key
]
157 id=str(uuid
.uuid1()),
163 nodes
= conn
['nodes']
164 for node
, node_keys
in [(node
, node
.keys()) for node
in nodes
]:
165 for node_key
in node_keys
:
166 topo_node
= openmano_nsd
['topology']['nodes'][node_key
]
167 if topo_node
['type'] == 'VNF':
168 cpref
= vld
.vnfd_connection_point_ref
.add()
169 cpref
.from_dict(dict(
170 member_vnf_index_ref
=vnf_member_index_dict
[node_key
],
171 vnfd_id_ref
=self
.get_vnfd_id(vnf_list
, topo_node
['VNF model']),
172 #vnfd_id_ref=topo_node['VNF model'],
173 vnfd_connection_point_ref
=node
[node_key
],
175 if key
!= 'control-net':
176 vld
.provider_network
.physical_network
= 'physnet_sriov'
177 vld
.provider_network
.overlay_type
= 'VLAN'
179 vnf_member_index_dict
= create_consituent_vnfds()
180 create_connections(vnf_member_index_dict
)
183 class RiftVnfd(RiftManoDescriptor
):
184 def __init__(self
, openmano
=None):
185 super().__init
__(openmano
)
186 self
.vnfd_catalog
= None
189 def find_external_connection(self
, vdu_name
, if_name
):
191 Find if the vdu interface has an external connection.
193 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
194 if 'external-connections' not in openmano_vnfd
:
197 ext_conn_list
= openmano_vnfd
['external-connections']
198 for ext_conn
in ext_conn_list
:
199 if ((ext_conn
['VNFC'] == vdu_name
) and
200 (ext_conn
['local_iface_name'] == if_name
)):
205 def openmano2rift(self
):
206 self
.descriptor
= RwVnfdYang
.YangData_Vnfd_VnfdCatalog()
207 vnfd
= self
.descriptor
.vnfd
.add()
209 vnfd
.id = str(uuid
.uuid1())
211 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
212 self
.name
= openmano_vnfd
['name']
213 vnfd
.name
= self
.name
214 if "description" in openmano_vnfd
:
215 vnfd
.description
= openmano_vnfd
['description']
217 # Parse and add all the external connection points
218 if 'external-connections' in openmano_vnfd
:
219 ext_conn_list
= openmano_vnfd
['external-connections']
221 for ext_conn
in ext_conn_list
:
223 if ext_conn
['name'] == 'eth0':
225 conn_point
= vnfd
.connection_point
.add()
226 conn_point
.name
= ext_conn
['name']
227 conn_point
.type_yang
= 'VPORT'
229 # TODO: Need a concrete example of how openmano descriptor
230 # uses internal connections.
231 if 'internal-connections' in openmano_vnfd
:
232 int_conn_list
= openmano_vnfd
['internal-connections']
234 def add_external_interfaces(vdu
, numa
):
235 if 'interfaces' not in numa
:
238 numa_if_list
= numa
['interfaces']
239 for numa_if
in numa_if_list
:
240 ext_conn
= self
.find_external_connection(vdu
.name
, numa_if
['name'])
244 ext_iface
= vdu
.external_interface
.add()
245 ext_iface
.name
= numa_if
['name']
246 ext_iface
.vnfd_connection_point_ref
= ext_conn
['name']
247 ext_iface
.virtual_interface
.vpci
= numa_if
['vpci']
248 if numa_if
['dedicated'] == 'no':
249 ext_iface
.virtual_interface
.type_yang
= 'SR_IOV'
251 ext_iface
.virtual_interface
.type_yang
= 'PCI_PASSTHROUGH'
253 vnfc_list
= openmano_vnfd
['VNFC']
254 for vnfc
in vnfc_list
:
257 id=str(uuid
.uuid1()),
259 image
=vnfc
['VNFC image'],
260 vm_flavor
={"storage_gb": vnfc
["disk"] if "disk" in vnfc
else 20},
262 if "description" in vnfc
:
263 vdu_dict
["description"] = vnfc
['description']
265 vdu
.from_dict(vdu_dict
)
267 vnfd
.mgmt_interface
.vdu_id
= vdu
.id
269 numa_list
= vnfc
['numas']
274 for numa
in numa_list
:
275 node
= vdu
.guest_epa
.numa_node_policy
.node
.add()
276 node
.id = numa_node_cnt
277 # node.memory_mb = int(numa['memory']) * 1024
280 memory
= memory
+ node
.memory_mb
281 # Need a better explanation of "cores", "paired-threads", "threads"
282 # in openmano descriptor. Particularly how they map to cpu and
283 # thread pinning policies
284 if 'paired-threads' in numa
:
285 vcpu_count
= vcpu_count
+ int(numa
['paired-threads']) * 2
288 vcpu_count
= vcpu_count
+ int(numa
['cores'])
290 add_external_interfaces(vdu
, numa
)
293 # vdu.vm_flavor.memory_mb = memory
294 vdu
.vm_flavor
.memory_mb
= 12 * 1024
295 vdu
.vm_flavor
.vcpu_count
= vcpu_count
296 vdu
.guest_epa
.numa_node_policy
.node_cnt
= numa_node_cnt
297 vdu
.guest_epa
.numa_node_policy
.mem_policy
= 'STRICT'
298 vdu
.guest_epa
.mempage_size
= 'LARGE'
299 vdu
.guest_epa
.cpu_pinning_policy
= 'DEDICATED'
300 vdu
.guest_epa
.cpu_thread_pinning_policy
= 'PREFER'
302 # TODO: Enable hypervisor epa
303 # vdu.hypervisor_epa.version = vnfc['hypervisor']['version']
304 # if vnfc['hypervisor']['type'] == 'QEMU-kvm':
305 # vdu.hypervisor_epa.type_yang = 'REQUIRE_KVM'
307 # vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
309 # TODO: Enable host epa
310 # vdu.host_epa.cpu_feature = vnfc['processor']['features']
312 # Parse the bridge interfaces
313 if 'bridge-ifaces' in vnfc
:
314 bridge_ifaces
= vnfc
['bridge-ifaces']
317 for bridge_iface
in bridge_ifaces
:
319 if bridge_iface
['name'] == 'eth0':
322 ext_conn
= self
.find_external_connection(vdu
.name
,
323 bridge_iface
['name'])
325 ext_iface
= vdu
.external_interface
.add()
326 ext_iface
.name
= bridge_iface
['name']
327 ext_iface
.vnfd_connection_point_ref
= ext_conn
['name']
328 if 'vpci' in bridge_iface
:
329 ext_iface
.virtual_interface
.vpci
= bridge_iface
['vpci']
330 ext_iface
.virtual_interface
.type_yang
= 'VIRTIO'
332 # set vpci information for the 'default' network
333 # TODO: This needs to be inferred gtom bridge ifaces,
334 # need input from TEF
335 vdu
.mgmt_vpci
= "0000:00:0a.0"
338 class OpenManoDescriptor(object):
339 def __init__(self
, yaml_file_hdl
):
340 self
.dictionary
= yaml
.load(yaml_file_hdl
)
344 """ The descriptor type (ns or vnf)"""
345 if 'vnf' in self
.dictionary
:
351 """ Dump the Descriptor out to stdout """
352 print(yaml
.dump(self
.dictionary
))
355 def is_writable_directory(dir_path
):
356 """ Returns True if dir_path is writable, False otherwise
359 dir_path - A directory path
361 if not os
.path
.exists(dir_path
):
362 raise ValueError("Directory does not exist: %s", dir_path
)
365 testfile
= tempfile
.TemporaryFile(dir=dir_path
)
373 def create_vnfs_from_yaml_files(yaml_file_hdls
):
374 """ Create a list of RiftVnfd instances from yaml file handles
377 yaml_file_hdls - OpenMano Yaml file handles
380 A list of RiftVnfd instances
383 for yaml_file_hdl
in yaml_file_hdls
:
384 openmano
= OpenManoDescriptor(yaml_file_hdl
)
385 yaml_file_hdl
.seek(0)
387 if openmano
.type != "vnf":
390 vnf
= RiftVnfd(openmano
)
397 def create_ns_from_yaml_files(yaml_file_hdls
, vnf_list
):
398 """ Create a list of RiftNS instances from yaml file handles
401 yaml_file_hdls - OpenMano Yaml file handles
402 vnf_list - list of RiftVnfd
405 A list of RiftNS instances
408 for yaml_file_hdl
in yaml_file_hdls
:
409 openmano
= OpenManoDescriptor(yaml_file_hdl
)
410 if openmano
.type != "ns":
413 net_svc
= RiftNS(openmano
)
414 net_svc
.openmano2rift(vnf_list
)
415 ns_list
.append(net_svc
)
420 def parse_args(argv
=sys
.argv
[1:]):
421 """ Parse the command line arguments
424 arv - The list of arguments to parse
427 Argparse Namespace instance
430 parser
= argparse
.ArgumentParser()
434 help="Directory to output converted descriptors",
439 choices
=['yaml', 'xml', 'json'],
441 help="Descriptor output format",
448 type=argparse
.FileType('r'),
449 help="OpenMano YAML Descriptor File",
452 args
= parser
.parse_args(argv
)
454 if not os
.path
.exists(args
.outdir
):
455 os
.makedirs(args
.outdir
)
457 if not is_writable_directory(args
.outdir
):
458 logging
.error("Directory %s is not writable", args
.outdir
)
464 def main(argv
=sys
.argv
[1:]):
465 args
= parse_args(argv
)
467 vnf_list
= create_vnfs_from_yaml_files(args
.yaml_file_hdls
)
468 ns_list
= create_ns_from_yaml_files(args
.yaml_file_hdls
, vnf_list
)
470 writer
= DescriptorFileWriter(
471 module_list
=['nsd', 'rw-nsd', 'vnfd', 'rw-vnfd'],
472 output_dir
=args
.outdir
,
473 output_format
=args
.format
,
476 for nw_svc
in ns_list
:
477 writer
.write_descriptor(nw_svc
, subdir
="nsd")
480 writer
.write_descriptor(vnf
, subdir
="vnfd")
483 if __name__
== "__main__":