2 # Copyright 2017 RIFT.IO Inc
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # Author(s): Nandan Sinha
26 gi
.require_version('RwVnfdYang', '1.0')
27 gi
.require_version('RwNsdYang', '1.0')
28 from gi
.repository
import (
37 import rift
.package
.icon
as icon
38 import rift
.tasklets
.rwlaunchpad
.onboard
as onboard
40 class PackageCopyError(Exception):
43 class CopyStatus(enum
.Enum
):
51 TaskStatus
= RwPkgMgmtYang
.TaskStatus
55 CopyStatus
.STARTED
: TaskStatus
.QUEUED
.value_nick
.upper(),
56 CopyStatus
.UNINITIATED
: TaskStatus
.QUEUED
.value_nick
.upper(),
57 CopyStatus
.IN_PROGRESS
: TaskStatus
.IN_PROGRESS
.value_nick
.upper(),
58 CopyStatus
.COMPLETED
: TaskStatus
.COMPLETED
.value_nick
.upper(),
59 CopyStatus
.FAILED
: TaskStatus
.FAILED
.value_nick
.upper(),
60 CopyStatus
.CANCELLED
: TaskStatus
.CANCELLED
.value_nick
.upper()
63 def __init__(self
, transaction_id
):
64 self
.transaction_id
= transaction_id
65 self
.state
= CopyStatus
.UNINITIATED
67 def set_state(self
, state
):
74 job
= RwPkgMgmtYang
.YangData_RwProject_Project_CopyJobs_Job
.from_dict({
75 "transaction_id": self
.transaction_id
,
76 "status": CopyMeta
.STATUS_MAP
[self
.state
]
81 """ Utility class to hold manifest information."""
82 def __init__(self
, project
, log
):
83 self
.tasklet_info
= project
.tasklet
.tasklet_info
84 self
.manifest
= self
.tasklet_info
.get_pb_manifest()
85 self
.use_ssl
= self
.manifest
.bootstrap_phase
.rwsecurity
.use_ssl
86 self
.ssl_cert
, self
.ssl_key
= None, None
88 self
.ssl_cert
= self
.manifest
.bootstrap_phase
.rwsecurity
.cert
89 self
.ssl_key
= self
.manifest
.bootstrap_phase
.rwsecurity
.key
93 def ssl_manifest(self
):
94 return (self
.use_ssl
, self
.ssl_cert
, self
.ssl_key
)
96 def get_onboarder(self
, host
="127.0.0.1", port
="8008"):
97 if not self
.onboarder
:
98 self
.onboarder
= onboard
.DescriptorOnboarder(self
.log
,
99 host
, port
, *self
.ssl_manifest())
100 return self
.onboarder
103 class PackageFileCopier
:
105 "vnfd": (RwVnfdYang
.YangData_Vnfd_VnfdCatalog_Vnfd
, 'vnfd rw-vnfd'),
106 "nsd" : (RwNsdYang
.YangData_Nsd_NsdCatalog_Nsd
, 'nsd rw-nsd')
110 def from_rpc_input(cls
, rpc_input
, project
, proxy
, log
=None):
112 rpc_input
.package_id
,
113 rpc_input
.package_type
,
114 rpc_input
.package_name
,
115 rpc_input
.project_name
,
128 self
.src_package_id
= pkg_id
129 self
.package_type
= pkg_type
.lower()
130 self
.dest_package_name
= pkg_name
131 self
.project_name
= proj_name
132 self
.manifest
= CopyManifest(project
, log
)
133 self
.dest_package_id
= str(uuid
.uuid4())
134 self
.transaction_id
= str(uuid
.uuid4())
137 self
.meta
= CopyMeta(self
.transaction_id
)
138 self
.src_package
= None
139 self
.dest_desc_msg
= None
143 """ Onboarder object to invoke REST endpoint calls."""
144 return self
.manifest
.get_onboarder()
148 """ Current status of operations."""
149 return self
.meta
.to_yang()
152 def descriptor_msg(self
):
153 """ Descriptor message of the generated copied descriptor."""
154 return self
.dest_desc_msg
156 # Start of delegate calls
157 def call_delegate(self
, event
):
158 if not self
.delegate
:
161 getattr(self
.delegate
, event
)(self
)
163 def _copy_tree(self
):
165 Locate directory tree of the source descriptor folder.
166 Copy directory tree to destination descriptor folder.
171 store
= self
.proxy
._get
_store
(self
.package_type
, \
172 self
.project_name
if self
.project_name
else None)
173 src_path
= store
._get
_package
_dir
(self
.src_package_id
)
174 self
.src_package
= store
.get_package(self
.src_package_id
)
176 self
.dest_copy_path
= os
.path
.join(
178 self
.dest_package_id
)
179 self
.log
.debug("Copying contents from {src} to {dest}".
180 format(src
=src_path
, dest
=self
.dest_copy_path
))
182 shutil
.copytree(src_path
, self
.dest_copy_path
)
183 # If there are icon files, also need to copy them in UI location
184 if os
.path
.exists(os
.path
.join(src_path
, "icons")):
185 src_icon_path
= os
.path
.join(
186 icon
.PackageIconExtractor
.DEFAULT_INSTALL_DIR
,
189 dest_icon_path
= os
.path
.join(
190 os
.path
.dirname(src_icon_path
),
191 self
.dest_package_id
)
193 self
.log
.debug("Copying UI icon location from {} to {}".format(src_icon_path
,
195 shutil
.copytree(src_icon_path
, dest_icon_path
)
197 def _create_descriptor_file(self
):
198 """ Update descriptor file for the newly copied descriptor catalog.
199 Get descriptor contents from REST endpoint, change some identifiers
200 and create a new descriptor yaml file from it.
202 # API call for the updated descriptor contents
203 src_desc_contents
= self
.onboarder
.get_updated_descriptor(self
.src_package
.descriptor_msg
, self
.project_name
)
205 # To generate the pb object, extract subtree in dict from "project-nsd:nsd" and root it
206 # under "nsd:nsd-catalog" (or vnfd)
207 root_element
= "{0}:{0}-catalog".format(self
.package_type
)
208 extract_sub_element
= "project-{0}:{0}".format(self
.package_type
, self
.package_type
)
209 src_desc_contents
[extract_sub_element
].update(
210 id =self
.dest_package_id
,
211 name
= self
.dest_package_name
,
212 short_name
= self
.dest_package_name
215 D
[root_element
] = {self
.package_type
: src_desc_contents
[extract_sub_element
]}
217 # Build the proto-buf gi object from generated JSON
218 json_desc_msg
= json
.dumps(D
)
219 self
.log
.debug("*** JSON contents: {}".format(json_desc_msg
))
220 desc_cls
, modules
= PackageFileCopier
.DESCRIPTOR_MAP
[self
.package_type
]
222 model
= RwYang
.Model
.create_libyang()
223 for module
in modules
.split():
224 model
.load_module(module
)
226 self
.dest_desc_msg
= desc_cls
.from_json(model
, json_desc_msg
, strict
=False)
228 # Write to yaml desc file
229 dest_desc_path
= os
.path
.join(self
.dest_copy_path
,
230 "{pkg_name}_{pkg_type}.yaml".format(pkg_name
=self
.dest_package_name
, pkg_type
=self
.package_type
))
231 with
open(dest_desc_path
, "w") as fh
:
232 fh
.write(self
.dest_desc_msg
.to_yaml(model
))
234 # Remove copied .yaml, if present
235 src_desc_file
= self
.src_package
.descriptor_file
236 copied_desc_file
= os
.path
.join(self
.dest_copy_path
, os
.path
.basename(src_desc_file
))
237 if os
.path
.exists(copied_desc_file
):
238 self
.log
.debug("Deleting copied yaml from old source %s" % (copied_desc_file
))
239 os
.remove(copied_desc_file
)
243 if self
.package_type
not in PackageFileCopier
.DESCRIPTOR_MAP
:
244 raise PackageCopyError("Package type {} not currently supported for copy operations".format(self
.package_type
))
247 self
._create
_descriptor
_file
()
248 self
.copy_succeeded()
250 except Exception as e
:
251 self
.log
.exception(str(e
))
256 def copy_failed(self
):
257 self
.meta
.set_state(CopyStatus
.FAILED
)
258 self
.call_delegate("on_download_failed")
260 def copy_progress(self
):
261 self
.meta
.set_state(CopyStatus
.IN_PROGRESS
)
262 self
.call_delegate("on_download_progress")
264 def copy_succeeded(self
):
265 self
.meta
.set_state(CopyStatus
.COMPLETED
)
266 self
.call_delegate("on_download_succeeded")
268 def copy_finished(self
):
269 self
.call_delegate("on_download_finished")