blob: dced2ab16dcc46ba50877531c25b4514bdde524e [file] [log] [blame]
Philip Joseph0f5e8c02017-03-03 01:54:51 +05301#!/usr/bin/env python3
2
3#
4# Copyright 2017 RIFT.IO Inc
5#
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
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
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.
17#
18
Philip Josephf4937572017-03-03 01:55:37 +053019import abc
20import asyncio
21import logging
22
23import gi
Philip Joseph99361662017-03-08 18:28:08 +053024gi.require_version('RwProjectManoYang', '1.0')
Philip Josephba63fbf2017-04-04 15:46:10 +053025gi.require_version('RwDts', '1.0')
Philip Josephf4937572017-03-03 01:55:37 +053026from gi.repository import (
Philip Joseph99361662017-03-08 18:28:08 +053027 RwProjectManoYang,
Philip Josephf4937572017-03-03 01:55:37 +053028 RwDts as rwdts,
29 ProtobufC,
Philip Joseph99361662017-03-08 18:28:08 +053030 RwTypes,
Philip Josephf4937572017-03-03 01:55:37 +053031)
32
33import rift.tasklets
34
Philip Joseph0f5e8c02017-03-03 01:54:51 +053035
36class ManoProjectError(Exception):
37 pass
38
39
40class ManoProjNameSetErr(ManoProjectError):
41 pass
42
43
44class ManoProjXpathNoProjErr(ManoProjectError):
45 pass
46
47
48class ManoProjXpathKeyErr(ManoProjectError):
49 pass
50
51
Philip Josephf4937572017-03-03 01:55:37 +053052class ManoProjXpathNotRootErr(ManoProjectError):
53 pass
54
55
56class ManoProjXpathPresentErr(ManoProjectError):
57 pass
58
59
60NS = 'rw-project'
61PROJECT = 'project'
62NS_PROJECT = '{}:{}'.format(NS, PROJECT)
63XPATH = '/{}'.format(NS_PROJECT)
64XPATH_LEN = len(XPATH)
65
66NAME = 'name'
67NAME_LEN = len(NAME)
68NS_NAME = '{}:{}'.format(NS, NAME)
69
70DEFAULT_PROJECT = 'default'
71DEFAULT_PREFIX = "{}[{}='{}']".format(XPATH,
72 NS_NAME,
73 DEFAULT_PROJECT)
74
75
Philip Joseph0f5e8c02017-03-03 01:54:51 +053076class ManoProject(object):
77 '''Class to handle the project name'''
78
Philip Josephf4937572017-03-03 01:55:37 +053079 log = None
Philip Joseph0f5e8c02017-03-03 01:54:51 +053080
81 @classmethod
Philip Josephf4937572017-03-03 01:55:37 +053082 def instance_from_xpath(cls, xpath, log):
Philip Joseph0f5e8c02017-03-03 01:54:51 +053083 name = cls.from_xpath(xpath, log)
84 if name is None:
85 return None
86
87 proj = ManoProject(log, name=name)
88 return proj
89
90 @classmethod
91 def from_xpath(cls, xpath, log):
92 log.debug("Get project name from {}".format(xpath));
93
Philip Josephf4937572017-03-03 01:55:37 +053094 if XPATH in xpath:
95 idx = xpath.find(XPATH)
Philip Joseph0f5e8c02017-03-03 01:54:51 +053096 if idx == -1:
97 msg = "Project not found in XPATH: {}".format(xpath)
98 log.error(msg)
99 raise ManoProjXpathNoProjErr(msg)
100
Philip Josephf4937572017-03-03 01:55:37 +0530101 sub = xpath[idx+XPATH_LEN:].strip()
102 if (len(sub) < NAME_LEN) or (sub[0] != '['):
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530103 msg = "Project name not found in XPath: {}".format(xpath)
104 log.error(msg)
105 raise ManoProjXpathKeyErr(msg)
106
107 sub = sub[1:].strip()
Philip Josephf4937572017-03-03 01:55:37 +0530108 idx = sub.find(NS_NAME)
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530109 if idx == -1:
Philip Josephf4937572017-03-03 01:55:37 +0530110 idx = sub.find(NAME)
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530111 if idx != 0:
112 msg = "Project name not found in XPath: {}".format(xpath)
113 log.error(msg)
114 raise ManoProjXpathKeyErr(msg)
115
116 idx = sub.find(']')
117 if idx == -1:
118 msg = "XPath is invalid: {}".format(xpath)
119 log.error(msg)
120 raise ManoProjXpathKeyErr(msg)
121
Philip Josephf4937572017-03-03 01:55:37 +0530122 sub = sub[:idx].strip()
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530123 try:
Philip Josephf4937572017-03-03 01:55:37 +0530124 log.debug("Key and value found: {}".format(sub))
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530125 k, n = sub.split("=", 2)
Philip Josephf4937572017-03-03 01:55:37 +0530126 name = n.strip(' \'"')
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530127 if name is None:
128 msg = "Project name is empty in XPath".format(xpath)
129 log.error(msg)
130 raise ManoProjXpathKeyErr (msg)
131
132 log.debug("Found project name {} from XPath {}".
133 format(name, xpath))
134 return name
135
136 except ValueError as e:
137 msg = "Project name not found in XPath: {}, exception: {}" \
138 .format(xpath, e)
139 log.exception(msg)
140 raise ManoProjXpathKeyErr(msg)
Philip Josephf4937572017-03-03 01:55:37 +0530141 else:
142 msg = "Project not found in XPATH: {}".format(xpath)
143 log.error(msg)
144 raise ManoProjXpathNoProjErr(msg)
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530145
Philip Josephf4937572017-03-03 01:55:37 +0530146 @classmethod
147 def get_log(cls):
148 if not cls.log:
149 cls.log = logging.getLogger('rw-mano-log.rw-project')
150 cls.log.setLevel(logging.ERROR)
151
152 @classmethod
153 def prefix_project(cls, xpath, project=None, log=None):
154 if log is None:
155 log = cls.get_log()
156
157 if project is None:
158 project = DEFAULT_PROJECT
159 proj_prefix = DEFAULT_PREFIX
160 else:
161 proj_prefix = "{}[{}='{}']".format(XPATH,
162 NS_NAME,
163 project)
164
165 log.debug("Add project {} to {}".format(project, xpath))
166
167 prefix = ''
168 suffix = xpath
169 idx = xpath.find('C,/')
170 if idx == -1:
171 idx = xpath.find('D,/')
172
173 suffix = xpath
174 if idx != -1:
175 prefix = xpath[:2]
176 suffix = xpath[2:]
177
178 if suffix[0] != '/':
179 msg = "Non-rooted xpath provided: {}".format(xpath)
180 log.error(msg)
181 raise ManoProjXpathNotRootErr(msg)
182
183 idx = suffix.find(XPATH)
184 if idx == 0:
185 name = cls.from_xpath(xpath, log)
186 if name == project:
Philip Joseph4f810f22017-03-07 23:09:10 +0530187 log.debug("Project already in the XPATH: {}".format(xpath))
Philip Josephf4937572017-03-03 01:55:37 +0530188 return xpath
189
190 else:
191 msg = "Different project {} already in XPATH {}". \
192 format(name, xpath)
193 log.error(msg)
194 raise ManoProjXpathPresentErr(msg)
195
196 ret = prefix + proj_prefix + suffix
197 log.debug("XPath with project: {}".format(ret))
198 return ret
199
200
201 def __init__(self, log, name=None, tasklet=None):
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530202 self._log = log
Philip Josephf4937572017-03-03 01:55:37 +0530203 self._name = None
204 self._prefix = None
205 self._pbcm = None
206 self._tasklet = None
207 self._dts = None
208 self._loop = None
209 self._log_hdl = None
210
211 # Track if the apply config was received
212 self._apply = False
213
214 if name:
215 self.name = name
216
217 def update(self, tasklet):
218 # Store the commonly used properties from a tasklet
219 self._tasklet = tasklet
220 self._log_hdl = tasklet.log_hdl
221 self._dts = tasklet.dts
222 self._loop = tasklet.loop
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530223
224 @property
225 def name(self):
226 return self._name
227
Philip Josephf4937572017-03-03 01:55:37 +0530228 @property
229 def log(self):
230 return self._log
231
232 @property
233 def prefix(self):
234 return self._prefix
235
236 @property
237 def pbcm(self):
238 return self._pbcm
239
240 @property
241 def config(self):
242 return self._pbcm.project_config
243
244 @property
245 def tasklet(self):
246 return self._tasklet
247
248 @property
249 def log_hdl(self):
250 return self._log_hdl
251
252 @property
253 def dts(self):
254 return self._dts
255
256 @property
257 def loop(self):
258 return self._loop
259
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530260 @name.setter
261 def name(self, value):
262 if self._name is None:
263 self._name = value
Philip Josephf4937572017-03-03 01:55:37 +0530264 self._prefix = "{}[{}='{}']".format(XPATH,
265 NS_NAME,
266 self._name)
Philip Joseph99361662017-03-08 18:28:08 +0530267 self._pbcm = RwProjectManoYang.YangData_RwProject_Project(
Philip Josephf4937572017-03-03 01:55:37 +0530268 name=self._name)
269
270 elif self._name == value:
271 self._log.debug("Setting the same name again for project {}".
272 format(value))
Philip Joseph0f5e8c02017-03-03 01:54:51 +0530273 else:
274 msg = "Project name already set to {}".format(self._name)
275 self._log.error(msg)
276 raise ManoProjNameSetErr(msg)
277
278 def set_from_xpath(self, xpath):
Philip Josephf4937572017-03-03 01:55:37 +0530279 self.name = ManoProject.from_xpath(xpath, self._log)
280
281 def add_project(self, xpath):
282 return ManoProject.prefix_project(xpath, log=self._log, project=self._name)
283
284 @abc.abstractmethod
285 @asyncio.coroutine
286 def delete_prepare(self):
287 self._log.debug("Delete prepare for project {}".format(self._name))
288 return True
289
290 @abc.abstractmethod
291 @asyncio.coroutine
292 def register(self):
293 msg = "Register not implemented for project type {}". \
294 format(self.__class__.__name__)
295 self._log.error(msg)
296 raise NotImplementedError(msg)
297
298 @abc.abstractmethod
299 def deregister(self):
300 msg = "De-register not implemented for project type {}". \
301 format(self.__class__.__name__)
302 self._log.error(msg)
303 raise NotImplementedError(msg)
304
305 def rpc_check(self, msg, xact_info=None):
306 '''Check if the rpc is for this project'''
307 try:
308 project = msg.project_name
309 except AttributeError as e:
310 project = DEFAULT_PROJECT
311
312 if project != self.name:
313 self._log.debug("Project {}: RPC is for different project {}".
314 format(self.name, project))
315 if xact_info:
316 xact_info.respond_xpath(rwdts.XactRspCode.ACK)
317 return False
318
319 return True
320
321 @asyncio.coroutine
322 def create_project(self, dts):
Philip Joseph99361662017-03-08 18:28:08 +0530323 proj_xpath = "C,{}/config".format(self.prefix)
Philip Josephf4937572017-03-03 01:55:37 +0530324 self._log.info("Creating project: {} with {}".
325 format(proj_xpath, self.config.as_dict()))
326
327 yield from dts.query_create(proj_xpath,
328 rwdts.XactFlag.ADVISE,
329 self.config)
330
331
332def get_add_delete_update_cfgs(dts_member_reg, xact, key_name):
333 #TODO: Check why this is getting called during project delete
334 if not dts_member_reg:
335 return [], [], []
336
337 # Unforunately, it is currently difficult to figure out what has exactly
338 # changed in this xact without Pbdelta support (RIFT-4916)
339 # As a workaround, we can fetch the pre and post xact elements and
340 # perform a comparison to figure out adds/deletes/updates
341 xact_cfgs = list(dts_member_reg.get_xact_elements(xact))
342 curr_cfgs = list(dts_member_reg.elements)
343
344 xact_key_map = {getattr(cfg, key_name): cfg for cfg in xact_cfgs}
345 curr_key_map = {getattr(cfg, key_name): cfg for cfg in curr_cfgs}
346
347 # Find Adds
348 added_keys = set(xact_key_map) - set(curr_key_map)
349 added_cfgs = [xact_key_map[key] for key in added_keys]
350
351 # Find Deletes
352 deleted_keys = set(curr_key_map) - set(xact_key_map)
353 deleted_cfgs = [curr_key_map[key] for key in deleted_keys]
354
355 # Find Updates
356 updated_keys = set(curr_key_map) & set(xact_key_map)
357 updated_cfgs = [xact_key_map[key] for key in updated_keys if xact_key_map[key] != curr_key_map[key]]
358
359 return added_cfgs, deleted_cfgs, updated_cfgs
360
361
362class ProjectConfigCallbacks(object):
363 def __init__(self,
364 on_add_apply=None, on_add_prepare=None,
365 on_delete_apply=None, on_delete_prepare=None):
366
367 @asyncio.coroutine
368 def prepare_noop(*args, **kwargs):
369 pass
370
371 def apply_noop(*args, **kwargs):
372 pass
373
374 self.on_add_apply = on_add_apply
375 self.on_add_prepare = on_add_prepare
376 self.on_delete_apply = on_delete_apply
377 self.on_delete_prepare = on_delete_prepare
378
379 for f in ('on_add_apply', 'on_delete_apply'):
380 ref = getattr(self, f)
381 if ref is None:
382 setattr(self, f, apply_noop)
383 continue
384
385 if asyncio.iscoroutinefunction(ref):
386 raise ValueError('%s cannot be a coroutine' % (f,))
387
388 for f in ('on_add_prepare', 'on_delete_prepare'):
389 ref = getattr(self, f)
390 if ref is None:
391 setattr(self, f, prepare_noop)
392 continue
393
394 if not asyncio.iscoroutinefunction(ref):
395 raise ValueError("%s must be a coroutine" % f)
396
397
398class ProjectDtsHandler(object):
Philip Josephb16bd102017-03-23 02:12:02 +0530399 XPATH = "C,{}/project-config".format(XPATH)
Philip Josephf4937572017-03-03 01:55:37 +0530400
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530401 def __init__(self, dts, log, callbacks, sub_config=True):
Philip Josephf4937572017-03-03 01:55:37 +0530402 self._dts = dts
403 self._log = log
404 self._callbacks = callbacks
405
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530406 if sub_config:
407 self.xpath = ProjectDtsHandler.XPATH
408 self._key = 'name_ref'
409 else:
410 self.xpath = "C,{}".format(XPATH)
411 self._key = 'name'
412
Philip Josephf4937572017-03-03 01:55:37 +0530413 self.reg = None
414 self.projects = []
415
416 @property
417 def log(self):
418 return self._log
419
420 @property
421 def dts(self):
422 return self._dts
423
424 def add_project(self, name):
Philip Joseph10c9a632017-04-13 15:10:29 +0530425 self._log.info("Adding project: {}".format(name))
Philip Josephf4937572017-03-03 01:55:37 +0530426
427 if name not in self.projects:
428 self._callbacks.on_add_apply(name)
429 self.projects.append(name)
430 else:
Philip Joseph10c9a632017-04-13 15:10:29 +0530431 self._log.error("Project already present: {}".
Philip Josephf4937572017-03-03 01:55:37 +0530432 format(name))
433
434 def delete_project(self, name):
435 self._log.info("Deleting project: {}".format(name))
436 if name in self.projects:
437 self._callbacks.on_delete_apply(name)
438 self.projects.remove(name)
439 else:
Philip Joseph10c9a632017-04-13 15:10:29 +0530440 self._log.error("Unrecognized project: {}".
Philip Josephf4937572017-03-03 01:55:37 +0530441 format(name))
442
443 def update_project(self, name):
444 """ Update an existing project
445
446 Currently, we do not take any action on MANO for this,
447 so no callbacks are defined
448
449 Arguments:
450 msg - The project config message
451 """
452 self._log.info("Updating project: {}".format(name))
453 if name in self.projects:
454 pass
455 else:
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530456 self.add_project(name)
Philip Josephf4937572017-03-03 01:55:37 +0530457
458 def register(self):
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530459 def on_init(acg, xact, scratch):
460 self._log.debug("on_init")
461 scratch["projects"] = {
462 "added": [],
463 "deleted": [],
464 "updated": [],
465 }
466 return scratch
467
Philip Josephf4937572017-03-03 01:55:37 +0530468 @asyncio.coroutine
469 def apply_config(dts, acg, xact, action, scratch):
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530470 self._log.debug("Got project apply config (xact: %s) (action: %s): %s",
471 xact, action, scratch)
Philip Josephf4937572017-03-03 01:55:37 +0530472
473 if xact.xact is None:
474 if action == rwdts.AppconfAction.INSTALL:
475 curr_cfg = self._reg.elements
476 for cfg in curr_cfg:
Philip Joseph10c9a632017-04-13 15:10:29 +0530477 self._log.error("NOT IMPLEMENTED: Project being re-added after restart: {}".
478 format(cfg))
479 # self.add_project(cfg.name)
480 raise NotImplementedError("Tasklet restart not supported")
Philip Josephf4937572017-03-03 01:55:37 +0530481 else:
Philip Josephf4937572017-03-03 01:55:37 +0530482 self._log.debug("No xact handle. Skipping apply config")
483
484 return
485
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530486 try:
487 add_cfgs = scratch["projects"]["added"]
488 except KeyError:
489 add_cfgs = []
490
491 try:
492 del_cfgs = scratch["projects"]["deleted"]
493 except KeyError:
494 del_cfgs = []
495
496 try:
497 update_cfgs = scratch["projects"]["updated"]
498 except KeyError:
499 update_cfgs = []
500
Philip Josephf4937572017-03-03 01:55:37 +0530501
502 # Handle Deletes
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530503 for name in del_cfgs:
504 self.delete_project(name)
Philip Josephf4937572017-03-03 01:55:37 +0530505
506 # Handle Adds
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530507 for name, msg in add_cfgs:
508 self.add_project(name)
Philip Josephf4937572017-03-03 01:55:37 +0530509
510 # Handle Updates
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530511 for name, msg in update_cfgs:
512 self.update_project(name)
Philip Josephf4937572017-03-03 01:55:37 +0530513
Philip Joseph99361662017-03-08 18:28:08 +0530514 return RwTypes.RwStatus.SUCCESS
515
Philip Josephf4937572017-03-03 01:55:37 +0530516 @asyncio.coroutine
517 def on_prepare(dts, acg, xact, xact_info, ks_path, msg, scratch):
518 """ Prepare callback from DTS for Project """
519
Philip Joseph99361662017-03-08 18:28:08 +0530520 action = xact_info.query_action
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530521 xpath = ks_path.to_xpath(RwProjectManoYang.get_schema())
522 self._log.debug("Project xpath: {}".format(xpath))
523 name = ManoProject.from_xpath(xpath, self._log)
Philip Joseph99361662017-03-08 18:28:08 +0530524
Philip Josephf4937572017-03-03 01:55:37 +0530525 self._log.debug("Project %s on_prepare config received (action: %s): %s",
526 name, xact_info.query_action, msg)
527
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530528 if action == rwdts.QueryAction.CREATE:
Philip Josephf4937572017-03-03 01:55:37 +0530529 if name in self.projects:
530 self._log.debug("Project {} already exists. Ignore request".
531 format(name))
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530532 else:
533 yield from self._callbacks.on_add_prepare(name)
534 scratch["projects"]["added"].append((name, msg))
Philip Josephf4937572017-03-03 01:55:37 +0530535
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530536 elif action == rwdts.QueryAction.UPDATE:
537 if name in self.projects:
Philip Joseph1426e5b2017-04-03 18:53:53 +0530538 scratch["projects"]["updated"].append((name, msg))
Philip Josephf4937572017-03-03 01:55:37 +0530539 else:
540 self._log.debug("Project {}: Invoking on_prepare add request".
541 format(name))
542 yield from self._callbacks.on_add_prepare(name)
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530543 scratch["projects"]["added"].append((name, msg))
Philip Josephf4937572017-03-03 01:55:37 +0530544
Philip Joseph99361662017-03-08 18:28:08 +0530545
Philip Josephf4937572017-03-03 01:55:37 +0530546 elif action == rwdts.QueryAction.DELETE:
547 # Check if the entire project got deleted
548 fref = ProtobufC.FieldReference.alloc()
549 fref.goto_whole_message(msg.to_pbcm())
550 if fref.is_field_deleted():
551 if name in self.projects:
552 rc = yield from self._callbacks.on_delete_prepare(name)
553 if not rc:
554 self._log.error("Project {} should not be deleted".
555 format(name))
556 xact_info.respond_xpath(rwdts.XactRspCode.NACK)
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530557 return
558
559 scratch["projects"]["deleted"].append(name)
Philip Josephf4937572017-03-03 01:55:37 +0530560 else:
561 self._log.warning("Delete on unknown project: {}".
562 format(name))
563
564 else:
565 self._log.error("Action (%s) NOT SUPPORTED", action)
566 xact_info.respond_xpath(rwdts.XactRspCode.NACK)
567 return
568
569 xact_info.respond_xpath(rwdts.XactRspCode.ACK)
570
571 self._log.debug("Registering for project config using xpath: %s",
572 ProjectDtsHandler.XPATH,
573 )
574
575 acg_handler = rift.tasklets.AppConfGroup.Handler(
576 on_apply=apply_config,
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530577 on_init=on_init)
Philip Josephf4937572017-03-03 01:55:37 +0530578
579 with self._dts.appconf_group_create(acg_handler) as acg:
580 self._reg = acg.register(
581 xpath=ProjectDtsHandler.XPATH,
582 flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY | rwdts.Flag.CACHE,
583 on_prepare=on_prepare,
584 )
585
Philip Joseph9bbec9d2017-03-29 17:11:32 +0530586
Philip Josephf4937572017-03-03 01:55:37 +0530587class ProjectHandler(object):
588 def __init__(self, tasklet, project_class, **kw):
589 self._tasklet = tasklet
590 self._log = tasklet.log
591 self._log_hdl = tasklet.log_hdl
592 self._dts = tasklet.dts
593 self._loop = tasklet.loop
594 self._class = project_class
595 self._kw = kw
596
597 self._log.debug("Creating project config handler")
598 self.project_cfg_handler = ProjectDtsHandler(
599 self._dts, self._log,
600 ProjectConfigCallbacks(
601 on_add_apply=self.on_project_added,
602 on_add_prepare=self.on_add_prepare,
603 on_delete_apply=self.on_project_deleted,
604 on_delete_prepare=self.on_delete_prepare,
605 )
606 )
607
608 def _get_tasklet_name(self):
609 return self._tasklet.tasklet_info.instance_name
610
611 def _get_project(self, name):
612 try:
613 proj = self._tasklet.projects[name]
614 except Exception as e:
615 self._log.exception("Project {} ({})not found for tasklet {}: {}".
616 format(name, list(self._tasklet.projects.keys()),
617 self._get_tasklet_name(), e))
618 raise e
619
620 return proj
621
622 def on_project_deleted(self, name):
623 self._log.debug("Project {} deleted".format(name))
624 try:
625 self._get_project(name).deregister()
626 except Exception as e:
627 self._log.exception("Project {} deregister for {} failed: {}".
628 format(name, self._get_tasklet_name(), e))
629
630 try:
631 proj = self._tasklet.projects.pop(name)
632 del proj
633 except Exception as e:
634 self._log.exception("Project {} delete for {} failed: {}".
635 format(name, self._get_tasklet_name(), e))
636
637 def on_project_added(self, name):
Philip Joseph10c9a632017-04-13 15:10:29 +0530638 if name not in self._tasklet.projects:
Philip Joseph10c9a632017-04-13 15:10:29 +0530639 try:
640 self._tasklet.projects[name] = \
641 self._class(name, self._tasklet, **(self._kw))
642 self._loop.create_task(self._get_project(name).register())
643
644 except Exception as e:
645 self._log.exception("Project {} create for {} failed: {}".
646 format(name, self._get_tasklet_name(), e))
647 raise e
648
Philip Josephf4937572017-03-03 01:55:37 +0530649 self._log.debug("Project {} added to tasklet {}".
650 format(name, self._get_tasklet_name()))
651 self._get_project(name)._apply = True
652
653 @asyncio.coroutine
654 def on_add_prepare(self, name):
655 self._log.debug("Project {} to be added to {}".
656 format(name, self._get_tasklet_name()))
Philip Joseph01b21e52017-04-21 12:23:36 +0530657 if name in self._tasklet.projects:
658 self._log.error("Project {} already exists for {}".
659 format(name, self._get_tasklet_name()))
Philip Josephf4937572017-03-03 01:55:37 +0530660
661 @asyncio.coroutine
662 def on_delete_prepare(self, name):
663 self._log.debug("Project {} being deleted for tasklet {}".
664 format(name, self._get_tasklet_name()))
665 rc = yield from self._get_project(name).delete_prepare()
666 return rc
667
668 def register(self):
669 self.project_cfg_handler.register()