Added all common modules in conductor directory
[optf/has.git] / conductor / conductor / common / models / plan.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 """Plan Model"""
21
22 import json
23 import time
24
25 from conductor.common.models import validate_uuid4
26 from conductor.common.music.model import base
27
28
29 def current_time_millis():
30     """Current time in milliseconds."""
31     return int(round(time.time() * 1000))
32
33
34 class Plan(base.Base):
35     """Plan model.
36
37     DO NOT use this class directly!
38
39     Only create Plan-based classes using:
40     base.create_dynamic_model(keyspace=KEYSPACE,
41         baseclass=Plan, classname=CLASS).
42     The table will be automatically created if it doesn't exist.
43     """
44
45     __tablename__ = "plans"
46     __keyspace__ = None
47
48     id = None  # pylint: disable=C0103
49     status = None
50     created = None
51     updated = None
52     name = None
53     timeout = None
54     recommend_max = None
55     message = None
56     template = None
57     translation = None
58     solution = None
59
60     # Status
61     TEMPLATE = "template"  # Template ready for translation
62     TRANSLATED = "translated"  # Translation ready for solving
63     SOLVING = "solving"  # Search for solutions in progress
64     # Search complete, solution with n>0 recommendations found
65     SOLVED = "solved"
66     # Search failed, no recommendations found
67     NOT_FOUND = "not found"
68     ERROR = "error"  # Error
69     # Solved, but reservation of resources in progress
70     RESERVING = "reserving"
71     # Final state, Solved and Reserved resources (if required)
72     DONE = "done"
73     STATUS = [TEMPLATE, TRANSLATED, SOLVING, SOLVED, NOT_FOUND,
74               ERROR, RESERVING, DONE, ]
75     WORKING = [TEMPLATE, TRANSLATED, SOLVING, RESERVING, ]
76     FINISHED = [SOLVED, NOT_FOUND, ERROR, DONE, ]
77
78     @classmethod
79     def schema(cls):
80         """Return schema."""
81         schema = {
82             'id': 'text',  # Plan ID in UUID4 format
83             'status': 'text',  # Plan status (see STATUS for valid values)
84             'created': 'bigint',  # Creation time in msec from epoch
85             'updated': 'bigint',  # Last update time in msec from epoch
86             'name': 'text',  # Plan name/alias
87             'timeout': 'int',  # Timeout in seconds
88             'recommend_max': 'int',  # Max recommendations
89             'message': 'text',  # Message (e.g., error or other info)
90             'template': 'text',  # Plan template
91             'translation': 'text',  # Translated template for the solver
92             'solution': 'text',  # The (ocean is the ultimate) solution (FZ)
93             'PRIMARY KEY': '(id)',
94         }
95         return schema
96
97     @classmethod
98     def atomic(cls):
99         """Use atomic operations"""
100         return False
101
102     @classmethod
103     def pk_name(cls):
104         """Primary key name"""
105         return 'id'
106
107     def pk_value(self):
108         """Primary key value"""
109         return self.id
110
111     @property
112     def error(self):
113         return self.status == self.ERROR
114
115     @property
116     def finished(self):
117         return self.status in self.FINISHED
118
119     @property
120     def solved(self):
121         return self.status == self.SOLUTION
122
123     @property
124     def done(self):
125         return self.status == self.DONE
126
127     @property
128     def timedout(self):
129         """Calculate if a plan has timed out"""
130         elapsed_msec = (current_time_millis() - self.created)
131         return elapsed_msec >= self.timeout * 1000
132
133     @property
134     def working(self):
135         return self.status in self.WORKING
136
137     def update(self):
138         """Update plan
139
140         Side-effect: Sets the updated field to the current time.
141         """
142         self.updated = current_time_millis()
143         super(Plan, self).update()
144
145     def values(self):
146         """Values"""
147         value_dict = {
148             'status': self.status,
149             'created': self.created,
150             'updated': self.updated,
151             'name': self.name,
152             'timeout': self.timeout,
153             'recommend_max': self.recommend_max,
154             'message': self.message,
155             'template': json.dumps(self.template),
156             'translation': json.dumps(self.translation),
157             'solution': json.dumps(self.solution),
158         }
159         if self.id:
160             value_dict['id'] = self.id
161         return value_dict
162
163     def __init__(self, name, timeout, recommend_max, template,
164                  id=None, created=None, updated=None, status=None,
165                  message=None, translation=None, solution=None, _insert=True):
166         """Initializer"""
167         super(Plan, self).__init__()
168         self.status = status or self.TEMPLATE
169         self.created = created or current_time_millis()
170         self.updated = updated or current_time_millis()
171         self.name = name
172         self.timeout = timeout
173         self.recommend_max = recommend_max
174         self.message = message or ""
175         if _insert:
176             if validate_uuid4(id):
177                 self.id = id
178             self.template = template or {}
179             self.translation = translation or {}
180             self.solution = solution or {}
181             self.insert()
182         else:
183             self.template = json.loads(template)
184             self.translation = json.loads(translation)
185             self.solution = json.loads(solution)
186
187     def __repr__(self):
188         """Object representation"""
189         return '<Plan {} ({})>'.format(self.name, self.id)
190
191     def __json__(self):
192         """JSON representation"""
193         json_ = {}
194         json_['id'] = self.id
195         json_['status'] = self.status
196         json_['created'] = self.created
197         json_['updated'] = self.updated
198         json_['name'] = self.name
199         json_['timeout'] = self.timeout
200         json_['recommend_max'] = self.recommend_max
201         json_['message'] = self.message
202         json_['template'] = self.template
203         json_['translation'] = self.translation
204         json_['solution'] = self.solution
205         return json_