4 # Copyright 2016-2017 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('RwProjectVnfdYang', '1.0')
32 gi
.require_version('RwProjectNsdYang', '1.0')
33 gi
.require_version('RwProjectYang', '1.0')
34 from gi
.repository
import (
36 RwProjectVnfdYang
as RwVnfdYang
,
37 RwProjectNsdYang
as RwNsdYang
,
41 logging
.basicConfig(level
=logging
.WARNING
)
42 logger
= logging
.getLogger("openmano2rift.py")
45 class UnknownVNFError(Exception):
49 class DescriptorFileWriter(object):
50 def __init__(self
, module_list
, output_dir
, output_format
):
51 self
._model
= RwYang
.Model
.create_libyang()
52 for module
in module_list
:
53 self
._model
.load_module(module
)
55 self
._output
_dir
= output_dir
56 self
._output
_format
= output_format
58 def _write_file(self
, file_name
, output
):
59 file_path
= os
.path
.join(self
._output
_dir
, file_name
)
60 dir_path
= os
.path
.dirname(file_path
)
61 if not os
.path
.exists(dir_path
):
64 with
open(file_path
, "w") as hdl
:
67 logger
.info("Wrote descriptor to %s", file_path
)
69 def _write_json(self
, descriptor
, subdir
):
71 '%s.json' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
72 descriptor
.descriptor
.to_json(self
._model
)
75 def _write_xml(self
, descriptor
, subdir
):
77 '%s.xml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
78 descriptor
.descriptor
.to_xml_v2(self
._model
, pretty_print
=True)
81 def _write_yaml(self
, descriptor
, subdir
):
83 '%s.yaml' % os
.path
.join(descriptor
.name
, subdir
, descriptor
.name
),
84 yaml
.dump(descriptor
.descriptor
.as_dict()),
87 def write_descriptor(self
, descriptor
, subdir
=""):
88 if self
._output
_format
== 'json':
89 self
._write
_json
(descriptor
, subdir
=subdir
)
91 elif self
._output
_format
== 'xml':
92 self
._write
_xml
(descriptor
, subdir
=subdir
)
94 elif self
._output
_format
== 'yaml':
95 self
._write
_yaml
(descriptor
, subdir
=subdir
)
98 class RiftManoDescriptor(object):
99 def __init__(self
, openmano
=None):
100 self
.openmano
= openmano
101 self
.descriptor
= None
104 class RiftNS(RiftManoDescriptor
):
105 def __init__(self
, openmano
=None):
106 super().__init
__(openmano
)
107 self
.nsd_catalog
= None
111 def get_vnfd_id(self
, vnf_list
, vnf_name
):
113 if vnf
.name
== vnf_name
:
116 # Didn't find the vnf just return the vnf_name
119 def openmano2rift(self
, vnf_list
):
120 self
.descriptor
= RwNsdYang
.YangData_RwProject_Project_NsdCatalog()
121 openmano_nsd
= self
.openmano
.dictionary
122 self
.name
= openmano_nsd
['name']
123 nsd
= self
.descriptor
.nsd
.add()
124 nsd
.id = str(uuid
.uuid1())
126 nsd
.short_name
= self
.name
127 nsd
.description
= openmano_nsd
['description']
129 nodes
= openmano_nsd
['topology']['nodes']
130 connections
= openmano_nsd
['topology']['connections']
132 def create_consituent_vnfds():
133 vnf_member_index_dict
= {}
135 vnfd_idx_gen
= itertools
.count(1)
138 if node
['type'] != 'VNF':
141 vnfd_idx
= next(vnfd_idx_gen
)
142 constituent_vnfd
= nsd
.constituent_vnfd
.add()
143 constituent_vnfd
.member_vnf_index
= vnfd_idx
144 constituent_vnfd
.vnfd_id_ref
= self
.get_vnfd_id(vnf_list
, node
['VNF model'])
145 vnf_member_index_dict
[key
] = vnfd_idx
147 return vnf_member_index_dict
149 def create_connections(vnf_member_index_dict
):
150 keys
= connections
.keys()
152 # TODO: Need clarification from TEF
153 # skip the mgmtnet in OpenMANO descriptor
156 conn
= connections
[key
]
159 id=str(uuid
.uuid1()),
165 nodes
= conn
['nodes']
166 for node
, node_keys
in [(node
, node
.keys()) for node
in nodes
]:
167 for node_key
in node_keys
:
168 topo_node
= openmano_nsd
['topology']['nodes'][node_key
]
169 if topo_node
['type'] == 'VNF':
170 cpref
= vld
.vnfd_connection_point_ref
.add()
171 cpref
.from_dict(dict(
172 member_vnf_index_ref
=vnf_member_index_dict
[node_key
],
173 vnfd_id_ref
=self
.get_vnfd_id(vnf_list
, topo_node
['VNF model']),
174 #vnfd_id_ref=topo_node['VNF model'],
175 vnfd_connection_point_ref
=node
[node_key
],
177 if key
!= 'control-net':
178 vld
.provider_network
.physical_network
= 'physnet_sriov'
179 vld
.provider_network
.overlay_type
= 'VLAN'
181 vnf_member_index_dict
= create_consituent_vnfds()
182 create_connections(vnf_member_index_dict
)
185 class RiftVnfd(RiftManoDescriptor
):
186 def __init__(self
, openmano
=None):
187 super().__init
__(openmano
)
188 self
.vnfd_catalog
= None
191 def find_external_connection(self
, vdu_name
, if_name
):
193 Find if the vdu interface has an external connection.
195 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
196 if 'external-connections' not in openmano_vnfd
:
199 ext_conn_list
= openmano_vnfd
['external-connections']
200 for ext_conn
in ext_conn_list
:
201 if ((ext_conn
['VNFC'] == vdu_name
) and
202 (ext_conn
['local_iface_name'] == if_name
)):
207 def openmano2rift(self
):
208 self
.descriptor
= RwVnfdYang
.YangData_RwProject_Project_VnfdCatalog()
209 vnfd
= self
.descriptor
.vnfd
.add()
211 vnfd
.id = str(uuid
.uuid1())
213 openmano_vnfd
= self
.openmano
.dictionary
['vnf']
214 self
.name
= openmano_vnfd
['name']
215 vnfd
.name
= self
.name
216 if "description" in openmano_vnfd
:
217 vnfd
.description
= openmano_vnfd
['description']
219 # Parse and add all the external connection points
220 if 'external-connections' in openmano_vnfd
:
221 ext_conn_list
= openmano_vnfd
['external-connections']
223 for ext_conn
in ext_conn_list
:
225 if ext_conn
['name'] == 'eth0':
227 conn_point
= vnfd
.connection_point
.add()
228 conn_point
.name
= ext_conn
['name']
229 conn_point
.type_yang
= 'VPORT'
231 # TODO: Need a concrete example of how openmano descriptor
232 # uses internal connections.
233 if 'internal-connections' in openmano_vnfd
:
234 int_conn_list
= openmano_vnfd
['internal-connections']
236 def add_external_interfaces(vdu
, numa
):
237 if 'interfaces' not in numa
:
240 numa_if_list
= numa
['interfaces']
241 for numa_if
in numa_if_list
:
242 ext_conn
= self
.find_external_connection(vdu
.name
, numa_if
['name'])
246 ext_iface
= vdu
.interface
.add()
247 ext_iface
.name
= numa_if
['name']
248 ext_iface
.type_yang
= 'EXTERNAL'
249 ext_iface
.external_connection_point_ref
= ext_conn
['name']
250 ext_iface
.virtual_interface
.vpci
= numa_if
['vpci']
251 if numa_if
['dedicated'] == 'no':
252 ext_iface
.virtual_interface
.type_yang
= 'SR_IOV'
254 ext_iface
.virtual_interface
.type_yang
= 'PCI_PASSTHROUGH'
256 vnfc_list
= openmano_vnfd
['VNFC']
257 for vnfc
in vnfc_list
:
260 id=str(uuid
.uuid1()),
262 image
=vnfc
['VNFC image'],
263 vm_flavor
={"storage_gb": vnfc
["disk"] if "disk" in vnfc
else 20},
265 if "description" in vnfc
:
266 vdu_dict
["description"] = vnfc
['description']
268 vdu
.from_dict(vdu_dict
)
270 vnfd
.mgmt_interface
.vdu_id
= vdu
.id
272 numa_list
= vnfc
['numas']
277 for numa
in numa_list
:
278 node
= vdu
.guest_epa
.numa_node_policy
.node
.add()
279 node
.id = numa_node_cnt
280 # node.memory_mb = int(numa['memory']) * 1024
283 memory
= memory
+ node
.memory_mb
284 # Need a better explanation of "cores", "paired-threads", "threads"
285 # in openmano descriptor. Particularly how they map to cpu and
286 # thread pinning policies
287 if 'paired-threads' in numa
:
288 vcpu_count
= vcpu_count
+ int(numa
['paired-threads']) * 2
291 vcpu_count
= vcpu_count
+ int(numa
['cores'])
293 add_external_interfaces(vdu
, numa
)
296 # vdu.vm_flavor.memory_mb = memory
297 vdu
.vm_flavor
.memory_mb
= 12 * 1024
298 vdu
.vm_flavor
.vcpu_count
= vcpu_count
299 vdu
.guest_epa
.numa_node_policy
.node_cnt
= numa_node_cnt
300 vdu
.guest_epa
.numa_node_policy
.mem_policy
= 'STRICT'
301 vdu
.guest_epa
.mempage_size
= 'LARGE'
302 vdu
.guest_epa
.cpu_pinning_policy
= 'DEDICATED'
303 vdu
.guest_epa
.cpu_thread_pinning_policy
= 'PREFER'
305 # TODO: Enable hypervisor epa
306 # vdu.hypervisor_epa.version = vnfc['hypervisor']['version']
307 # if vnfc['hypervisor']['type'] == 'QEMU-kvm':
308 # vdu.hypervisor_epa.type_yang = 'REQUIRE_KVM'
310 # vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
312 # TODO: Enable host epa
313 # vdu.host_epa.cpu_feature = vnfc['processor']['features']
315 # Parse the bridge interfaces
316 if 'bridge-ifaces' in vnfc
:
317 bridge_ifaces
= vnfc
['bridge-ifaces']
320 for bridge_iface
in bridge_ifaces
:
322 if bridge_iface
['name'] == 'eth0':
325 ext_conn
= self
.find_external_connection(vdu
.name
,
326 bridge_iface
['name'])
328 ext_iface
= vdu
.interface
.add()
329 ext_iface
.name
= bridge_iface
['name']
330 ext_iface
.type_yang
= 'EXTERNAL'
331 ext_iface
.external_connection_point_ref
= ext_conn
['name']
332 if 'vpci' in bridge_iface
:
333 ext_iface
.virtual_interface
.vpci
= bridge_iface
['vpci']
334 ext_iface
.virtual_interface
.type_yang
= 'VIRTIO'
336 # set vpci information for the 'default' network
337 # TODO: This needs to be inferred gtom bridge ifaces,
338 # need input from TEF
339 vdu
.mgmt_vpci
= "0000:00:0a.0"
342 class OpenManoDescriptor(object):
343 def __init__(self
, yaml_file_hdl
):
344 self
.dictionary
= yaml
.load(yaml_file_hdl
)
348 """ The descriptor type (ns or vnf)"""
349 if 'vnf' in self
.dictionary
:
355 """ Dump the Descriptor out to stdout """
356 print(yaml
.dump(self
.dictionary
))
359 def is_writable_directory(dir_path
):
360 """ Returns True if dir_path is writable, False otherwise
363 dir_path - A directory path
365 if not os
.path
.exists(dir_path
):
366 raise ValueError("Directory does not exist: %s", dir_path
)
369 testfile
= tempfile
.TemporaryFile(dir=dir_path
)
377 def create_vnfs_from_yaml_files(yaml_file_hdls
):
378 """ Create a list of RiftVnfd instances from yaml file handles
381 yaml_file_hdls - OpenMano Yaml file handles
384 A list of RiftVnfd instances
387 for yaml_file_hdl
in yaml_file_hdls
:
388 openmano
= OpenManoDescriptor(yaml_file_hdl
)
389 yaml_file_hdl
.seek(0)
391 if openmano
.type != "vnf":
394 vnf
= RiftVnfd(openmano
)
401 def create_ns_from_yaml_files(yaml_file_hdls
, vnf_list
):
402 """ Create a list of RiftNS instances from yaml file handles
405 yaml_file_hdls - OpenMano Yaml file handles
406 vnf_list - list of RiftVnfd
409 A list of RiftNS instances
412 for yaml_file_hdl
in yaml_file_hdls
:
413 openmano
= OpenManoDescriptor(yaml_file_hdl
)
414 if openmano
.type != "ns":
417 net_svc
= RiftNS(openmano
)
418 net_svc
.openmano2rift(vnf_list
)
419 ns_list
.append(net_svc
)
424 def parse_args(argv
=sys
.argv
[1:]):
425 """ Parse the command line arguments
428 arv - The list of arguments to parse
431 Argparse Namespace instance
434 parser
= argparse
.ArgumentParser()
438 help="Directory to output converted descriptors",
443 choices
=['yaml', 'xml', 'json'],
445 help="Descriptor output format",
452 type=argparse
.FileType('r'),
453 help="OpenMano YAML Descriptor File",
456 args
= parser
.parse_args(argv
)
458 if not os
.path
.exists(args
.outdir
):
459 os
.makedirs(args
.outdir
)
461 if not is_writable_directory(args
.outdir
):
462 logging
.error("Directory %s is not writable", args
.outdir
)
468 def main(argv
=sys
.argv
[1:]):
469 args
= parse_args(argv
)
471 vnf_list
= create_vnfs_from_yaml_files(args
.yaml_file_hdls
)
472 ns_list
= create_ns_from_yaml_files(args
.yaml_file_hdls
, vnf_list
)
474 # TODO (Philip): Relook at the model generation
475 writer
= DescriptorFileWriter(
476 module_list
=['rw-project', 'project-nsd', 'rw-project-nsd', 'project-vnfd', 'rw-project-vnfd'],
477 output_dir
=args
.outdir
,
478 output_format
=args
.format
,
481 for nw_svc
in ns_list
:
482 writer
.write_descriptor(nw_svc
, subdir
="nsd")
485 writer
.write_descriptor(vnf
, subdir
="vnfd")
488 if __name__
== "__main__":