Merge "Added reservation directory to the repository"
[optf/has.git] / conductor / conductor / controller / translator_svc.py
1 #
2 # -------------------------------------------------------------------------
3 #   Copyright (c) 2015-2017 AT&T Intellectual Property
4 #
5 #   Licensed under the Apache License, Version 2.0 (the "License");
6 #   you may not use this file except in compliance with the License.
7 #   You may obtain a copy of the License at
8 #
9 #       http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #   Unless required by applicable law or agreed to in writing, software
12 #   distributed under the License is distributed on an "AS IS" BASIS,
13 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 #   See the License for the specific language governing permissions and
15 #   limitations under the License.
16 #
17 # -------------------------------------------------------------------------
18 #
19
20 import time
21
22 import cotyledon
23 import futurist
24 from oslo_config import cfg
25 from oslo_log import log
26
27 from conductor.common.music import api
28 from conductor.common.music import messaging as music_messaging
29 from conductor.controller import translator
30 from conductor.i18n import _LE, _LI
31 from conductor import messaging
32
33 LOG = log.getLogger(__name__)
34
35 CONF = cfg.CONF
36
37 CONTROLLER_OPTS = [
38     cfg.IntOpt('polling_interval',
39                default=1,
40                min=1,
41                help='Time between checking for new plans. '
42                     'Default value is 1.'),
43 ]
44
45 CONF.register_opts(CONTROLLER_OPTS, group='controller')
46
47
48 class TranslatorService(cotyledon.Service):
49     """Template Translator service.
50
51     This service looks for untranslated templates and
52     preps them for solving by the Solver service.
53     """
54
55     # This will appear in 'ps xaf'
56     name = "Template Translator"
57
58     def __init__(self, worker_id, conf, **kwargs):
59         """Initializer"""
60         LOG.debug("%s" % self.__class__.__name__)
61         super(TranslatorService, self).__init__(worker_id)
62         self._init(conf, **kwargs)
63         self.running = True
64
65     def _init(self, conf, **kwargs):
66         self.conf = conf
67         self.Plan = kwargs.get('plan_class')
68         self.kwargs = kwargs
69
70         # Set up the RPC service(s) we want to talk to.
71         self.data_service = self.setup_rpc(conf, "data")
72
73         # Set up Music access.
74         self.music = api.API()
75
76     def _gracefully_stop(self):
77         """Gracefully stop working on things"""
78         pass
79
80     def _restart(self):
81         """Prepare to restart the service"""
82         pass
83
84     def setup_rpc(self, conf, topic):
85         """Set up the RPC Client"""
86         # TODO(jdandrea): Put this pattern inside music_messaging?
87         transport = messaging.get_transport(conf=conf)
88         target = music_messaging.Target(topic=topic)
89         client = music_messaging.RPCClient(conf=conf,
90                                            transport=transport,
91                                            target=target)
92         return client
93
94     def translate(self, plan):
95         """Translate the plan to a format the solver can use"""
96         # Update the translation field and set status to TRANSLATED.
97         try:
98             LOG.info(_LI("Requesting plan {} translation").format(
99                 plan.id))
100             trns = translator.Translator(
101                 self.conf, plan.name, plan.id, plan.template)
102             trns.translate()
103             if trns.ok:
104                 plan.translation = trns.translation
105                 plan.status = self.Plan.TRANSLATED
106                 LOG.info(_LI(
107                     "Plan {} translated. Ready for solving").format(
108                     plan.id))
109             else:
110                 plan.message = trns.error_message
111                 plan.status = self.Plan.ERROR
112                 LOG.error(_LE(
113                     "Plan {} translation error encountered").format(
114                     plan.id))
115         except Exception as ex:
116             template = "An exception of type {0} occurred, arguments:\n{1!r}"
117             plan.message = template.format(type(ex).__name__, ex.args)
118             plan.status = self.Plan.ERROR
119
120         plan.update()
121
122     def __check_for_templates(self):
123         """Wait for the polling interval, then do the real template check."""
124
125         # Wait for at least poll_interval sec
126         polling_interval = self.conf.controller.polling_interval
127         time.sleep(polling_interval)
128
129         # Look for plans with the status set to TEMPLATE
130         plans = self.Plan.query.all()
131         for plan in plans:
132             # If there's a template to be translated, do it!
133             if plan.status == self.Plan.TEMPLATE:
134                 self.translate(plan)
135                 break
136             elif plan.timedout:
137                 # Move plan to error status? Create a new timed-out status?
138                 # todo(snarayanan)
139                 continue
140
141     def run(self):
142         """Run"""
143         LOG.debug("%s" % self.__class__.__name__)
144
145         # Look for templates to translate from within a thread
146         executor = futurist.ThreadPoolExecutor()
147         while self.running:
148             fut = executor.submit(self.__check_for_templates)
149             fut.result()
150         executor.shutdown()
151
152     def terminate(self):
153         """Terminate"""
154         LOG.debug("%s" % self.__class__.__name__)
155         self.running = False
156         self._gracefully_stop()
157         super(TranslatorService, self).terminate()
158
159     def reload(self):
160         """Reload"""
161         LOG.debug("%s" % self.__class__.__name__)
162         self._restart()