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.
18 Project Manager tasklet is responsible for managing the Projects
19 configurations required for Role Based Access Control feature.
26 gi
.require_version('RwDts', '1.0')
27 from gi
.repository
import (
34 from rift
.mano
.utils
.project
import (
36 get_add_delete_update_cfgs
,
37 ProjectConfigCallbacks
,
41 class ProjectDtsHandler(object):
42 XPATH
= "C,/{}".format(NS_PROJECT
)
44 def __init__(self
, dts
, log
, callbacks
):
47 self
._callbacks
= callbacks
60 def add_project(self
, cfg
):
62 self
.log
.info("Adding project: {}".format(name
))
64 if name
not in self
.projects
:
65 self
._callbacks
.on_add_apply(name
, cfg
)
66 self
.projects
.append(name
)
68 self
.log
.error("Project already present: {}".
71 def delete_project(self
, name
):
72 self
._log
.info("Deleting project: {}".format(name
))
73 if name
in self
.projects
:
74 self
._callbacks
.on_delete_apply(name
)
75 self
.projects
.remove(name
)
77 self
.log
.error("Unrecognized project: {}".
80 def update_project(self
, cfg
):
81 """ Update an existing project
83 Currently, we do not take any action on MANO for this,
84 so no callbacks are defined
87 msg - The project config message
90 self
._log
.info("Updating project: {}".format(name
))
91 if name
in self
.projects
:
94 self
.log
.error("Unrecognized project: {}".
99 def apply_config(dts
, acg
, xact
, action
, scratch
):
100 self
._log
.debug("Got project apply config (xact: %s) (action: %s)", xact
, action
)
102 if xact
.xact
is None:
103 if action
== rwdts
.AppconfAction
.INSTALL
:
104 curr_cfg
= self
._reg
.elements
106 self
._log
.debug("Project being re-added after restart.")
107 self
.add_project(cfg
.name
)
109 # When RIFT first comes up, an INSTALL is called with the current config
110 # Since confd doesn't actally persist data this never has any data so
112 self
._log
.debug("No xact handle. Skipping apply config")
116 add_cfgs
, delete_cfgs
, update_cfgs
= get_add_delete_update_cfgs(
117 dts_member_reg
=self
._reg
,
123 for cfg
in delete_cfgs
:
124 self
.delete_project(cfg
.name
)
128 self
.add_project(cfg
)
131 for cfg
in update_cfgs
:
132 self
.update_project(cfg
)
134 return RwTypes
.RwStatus
.SUCCESS
137 def on_prepare(dts
, acg
, xact
, xact_info
, ks_path
, msg
, scratch
):
138 """ Prepare callback from DTS for Project """
140 action
= xact_info
.query_action
143 self
._log
.debug("Project %s on_prepare config received (action: %s): %s",
144 name
, xact_info
.query_action
, msg
)
146 if action
in [rwdts
.QueryAction
.CREATE
, rwdts
.QueryAction
.UPDATE
]:
147 if name
in self
.projects
:
148 self
._log
.debug("Project {} already exists. Ignore request".
152 self
._log
.debug("Project {}: Invoking on_prepare add request".
154 yield from self
._callbacks
.on_add_prepare(name
, msg
)
157 elif action
== rwdts
.QueryAction
.DELETE
:
158 # Check if the entire project got deleted
159 fref
= ProtobufC
.FieldReference
.alloc()
160 fref
.goto_whole_message(msg
.to_pbcm())
161 if fref
.is_field_deleted():
162 if name
in self
.projects
:
163 rc
= yield from self
._callbacks
.on_delete_prepare(name
)
165 self
._log
.error("Project {} should not be deleted".
167 xact_info
.respond_xpath(rwdts
.XactRspCode
.NACK
)
170 self
._log
.warning("Delete on unknown project: {}".
174 self
._log
.error("Action (%s) NOT SUPPORTED", action
)
175 xact_info
.respond_xpath(rwdts
.XactRspCode
.NACK
)
178 xact_info
.respond_xpath(rwdts
.XactRspCode
.ACK
)
180 self
._log
.debug("Registering for project config using xpath: %s",
181 ProjectDtsHandler
.XPATH
)
183 acg_handler
= rift
.tasklets
.AppConfGroup
.Handler(
184 on_apply
=apply_config
,
187 with self
._dts
.appconf_group_create(acg_handler
) as acg
:
188 self
._reg
= acg
.register(
189 xpath
=ProjectDtsHandler
.XPATH
,
190 flags
=rwdts
.Flag
.SUBSCRIBER | rwdts
.Flag
.DELTA_READY | rwdts
.Flag
.CACHE
,
191 on_prepare
=on_prepare
,
195 class ProjectHandler(object):
196 def __init__(self
, tasklet
, project_class
):
197 self
._tasklet
= tasklet
198 self
._log
= tasklet
.log
199 self
._log
_hdl
= tasklet
.log_hdl
200 self
._dts
= tasklet
.dts
201 self
._loop
= tasklet
.loop
202 self
._class
= project_class
204 self
._log
.debug("Creating project config handler")
205 self
.project_cfg_handler
= ProjectDtsHandler(
206 self
._dts
, self
._log
,
207 ProjectConfigCallbacks(
208 on_add_apply
=self
.on_project_added
,
209 on_add_prepare
=self
.on_add_prepare
,
210 on_delete_apply
=self
.on_project_deleted
,
211 on_delete_prepare
=self
.on_delete_prepare
,
215 def _get_tasklet_name(self
):
216 return self
._tasklet
.tasklet_info
.instance_name
218 def _get_project(self
, name
):
220 proj
= self
._tasklet
.projects
[name
]
221 except Exception as e
:
222 self
._log
.exception("Project {} ({})not found for tasklet {}: {}".
223 format(name
, list(self
._tasklet
.projects
.keys()),
224 self
._get
_tasklet
_name
(), e
))
229 def on_project_deleted(self
, name
):
230 self
._log
.debug("Project {} deleted".format(name
))
232 self
._get
_project
(name
).deregister()
233 except Exception as e
:
234 self
._log
.exception("Project {} deregister for {} failed: {}".
235 format(name
, self
._get
_tasklet
_name
(), e
))
238 proj
= self
._tasklet
.projects
.pop(name
)
240 except Exception as e
:
241 self
._log
.exception("Project {} delete for {} failed: {}".
242 format(name
, self
._get
_tasklet
_name
(), e
))
244 def on_project_added(self
, name
, cfg
):
245 self
._log
.debug("Project {} added to tasklet {}".
246 format(name
, self
._get
_tasklet
_name
()))
247 self
._get
_project
(name
)._apply
= True
250 def on_add_prepare(self
, name
, msg
):
251 self
._log
.debug("Project {} to be added to {}".
252 format(name
, self
._get
_tasklet
_name
()))
255 self
._tasklet
.projects
[name
] = \
256 self
._class
(name
, self
._tasklet
)
257 except Exception as e
:
258 self
._log
.exception("Project {} create for {} failed: {}".
259 format(name
, self
._get
_tasklet
_name
(), e
))
262 yield from self
._get
_project
(name
).register()
263 except Exception as e
:
264 self
._log
.exception("Project {} register for tasklet {} failed: {}".
265 format(name
, self
._get
_tasklet
_name
(), e
))
267 self
._log
.debug("Project {} added to {}".
268 format(name
, self
._get
_tasklet
_name
()))
271 def on_delete_prepare(self
, name
):
272 self
._log
.debug("Project {} being deleted for tasklet {}".
273 format(name
, self
._get
_tasklet
_name
()))
274 rc
= yield from self
._get
_project
(name
).delete_prepare()
278 self
.project_cfg_handler
.register()