Updated Service Directory url and cleaned some comments
[unfold.git] / sla / slaclient / templates / fed4fire / fed4fire.py
1 # -*- coding: utf-8 -*-\r
2 """Template system for fed4fire project.\r
3 \r
4 The specific template system is configured with the factory module variable.\r
5 \r
6 By default, it is set to use django.\r
7 \r
8 Each implementation must define a factory module/object, defining:\r
9 * slaagreement()\r
10 * slatemplate()\r
11 \r
12 that returns a slaclient.templates.Template-compliant object that performs\r
13 the actual render.\r
14 \r
15 This module defines two facade methods:\r
16 * render_slaagreement(data)\r
17 * render_slatemplate(data)\r
18 \r
19 and the corresponding input classes:\r
20 * AgreementInput\r
21 * TemplateInput\r
22 \r
23 Usage:\r
24     # Thread safe\r
25     import sla.slaclient.templates.fed4fire\r
26     data = sla.slaclient.templates.fed4fire.TemplateInput(template_id="template-test")\r
27     t = sla.slaclient.templates.fed4fire.django.Factory().slatemplate()\r
28     slatemplate_xml = t.render(data)\r
29 \r
30     # Non thread safe\r
31     import sla.slaclient.templates.fed4fire\r
32     data = sla.slaclient.templates.fed4fire.TemplateInput(template_id="template-test")\r
33     slatemplate_xml = sla.slaclient.templates.fed4fire.render_slatemplate(data)\r
34 \r
35 Notes about agreements in fed4fire:\r
36     The ws-agreement specification does not address where to place the name/id\r
37     of the service (as known outside SLA) being defined in the\r
38     agreement/template xml. So, it has been defined an element\r
39     wsag:Context/sla:Service, whose text is the name/id of the service. This\r
40     is known here as serviceId.\r
41 \r
42     An agreement/template can represent zero or more than one existing services.\r
43     The guarantee terms, service description terms, etc, use the attribute\r
44     serviceName to reference (internally in the xml) the service. So, there\r
45     could be more than one serviceName in a xml (as opposed to the former\r
46     serviceId). In fed4fire, there is only one service per agreement, so we\r
47     can give serviceId and serviceName the same value.\r
48 \r
49     A ServiceReference defines how a serviceName is known externally: a\r
50     service reference can be a name, a location, a structure containing both...\r
51 \r
52     The service properties are a set of variables that are used in the guarantee\r
53     terms contraints. So, for example, if a constraint is : "uptime < 90", we\r
54     can have 2 service properties: ActualUptime and DesiredUptime. And the\r
55     constraint will be "ActualUptime < DesiredUptime". This is the theory. But\r
56     we're not going to use the service properties this way. We will not use the\r
57     thresholds as service properties; only the actual metric. So, in this case,\r
58     the service property is defined in ws-agreement as:\r
59 \r
60         <wsag:Variable Name="Uptime" Metric="xs:double">\r
61             <wsag:Location>service-ping/Uptime</wsag:Location>\r
62         </wsag:Variable>\r
63 \r
64     The "location" is the strange value here. Ws-agreement says that it is a\r
65     "structural reference" to the place where to find the actual value of the\r
66     metric. The examples I've found are references to the\r
67     ServiceDescriptionTerms in the agreement itself. We are not using SDTs\r
68     (they are used to describe the service to be instantiated), so we can\r
69     extrapolate the location as the "abstract location of the metric".\r
70 \r
71     In summary, in fed4fire, the service properties will hold the metrics being\r
72     monitored for a service.\r
73 \r
74     And the guarantee terms hold the constraints that are being enforced for\r
75     the service in this agreement (maybe we are only interested in enforcing\r
76     one of the metrics).\r
77 \r
78     A guarantee term is defined as:\r
79         <wsag:GuaranteeTerm Name="GT_ResponseTime">\r
80             <wsag:ServiceScope ServiceName="service-ping"/>\r
81             <wsag:ServiceLevelObjective>\r
82                 <wsag:KPITarget>\r
83                     <wsag:KPIName>Uptime</wsag:KPIName>\r
84                     <wsag:CustomServiceLevel>\r
85                         {"constraint" : "Uptime BETWEEN (90, 100)"}\r
86                     </wsag:CustomServiceLevel>\r
87                 </wsag:KPITarget>\r
88             </wsag:ServiceLevelObjective>\r
89         </wsag:GuaranteeTerm>\r
90 \r
91     * Name is a name for the guarantee term. In fed4fire, the name will have the\r
92       value "GT_<metric_name>"\r
93     * ServiceName is an internal reference in the agreement to the service\r
94       being enforced, as an agreement can created for more than one service.\r
95       In fed4fire, to my knowledge, one service: one agreement, so this service\r
96       name is not really important.\r
97     * KpiName is a name given to the constraint, and I am using the same name\r
98       as the service property used in the constraint. This makes more sense\r
99       when using thresholds as service properties (e.g., a kpi called\r
100       "uptime" could be defined as :\r
101       "actual_uptime BETWEEN(lower_uptime, upper_uptime)").\r
102 \r
103     The CustomServiceLevel is not specified by ws-agreement, so it's something\r
104       to be defined by the implementation.\r
105 \r
106 """\r
107 \r
108 from sla.slaclient import wsag_model\r
109 import json\r
110 \r
111 from sla.slaclient.templates.fed4fire.django.factory import Factory\r
112 factory = Factory()\r
113 \r
114 \r
115 def _getfactory():\r
116     #\r
117     # Hardwired above to avoid multheading issues. This will need some\r
118     # refactoring if the factory really needs to be configurable.\r
119     #\r
120 \r
121     global factory\r
122     #if factory is None:\r
123     #    from slaclient.templates.fed4fire.django.factory import Factory\r
124     #    factory = Factory()\r
125     return factory\r
126 \r
127 \r
128 def render_slaagreement(data):\r
129     """Generate a sla agreement based on the supplied data.\r
130 \r
131     :type data: AgreementInput\r
132     """\r
133     print "render_slaagreement"\r
134     template = _getfactory().slaagreement()\r
135     #pdb.set_trace()\r
136     rendered = template.render(data)\r
137     return rendered\r
138 \r
139 \r
140 def render_slatemplate(data):\r
141     """Generate a sla template based on the supplied data.\r
142 \r
143     :type data: TemplateInput\r
144     """\r
145     template = _getfactory().slatemplate()\r
146     return template.render(data)\r
147 \r
148 \r
149 class TemplateInput(object):\r
150 \r
151     def __init__(self,\r
152                  template_id="",\r
153                  template_name="",\r
154                  provider="",\r
155                  service_id="",\r
156                  expiration_time=None,\r
157                  service_properties=()):\r
158         """Input data to the template for generating a sla-template.\r
159 \r
160         :param str template_id: optional TemplateId. If not specified, the\r
161           SlaManager should provide one.\r
162         :param str template_name: optional name for the template.\r
163         :param str service_id: Domain id/name of the service.\r
164         :param str provider: optional Resource Id of the provider party in the\r
165           agreement. The provider must exist previously in the SlaManager.\r
166         :param expiration_time: optional expiration time of this template.\r
167         :type expiration_time: datetime.datetime\r
168         :param service_properties: Metrics that the provider is able to\r
169           monitor for this service.\r
170         :type service_properties: list[slaclient.wsag_model.Agreement.Property]\r
171         """\r
172         self.template_id = template_id\r
173         self.template_name = template_name\r
174         self.service_id = service_id\r
175         self.provider = provider\r
176         self.expiration_time = expiration_time\r
177         self.expiration_time_iso = \\r
178             expiration_time.isoformat() if expiration_time else None\r
179         self.service_properties = service_properties\r
180 \r
181     def __repr__(self):\r
182         s = "<TemplateInput(template_id={}, template_name={})" \\r
183             "service_id={}, provider={}, expiration_time={}, " \\r
184             "service_properties={}>"\r
185         return s.format(\r
186             self.template_id,\r
187             self.template_name,\r
188             self.service_id,\r
189             self.provider,\r
190             self.expiration_time_iso,\r
191             repr(self.service_properties)\r
192         )\r
193 \r
194 \r
195 class AgreementInput(object):\r
196 \r
197     class GuaranteeTerm(object):\r
198 \r
199         class GuaranteeScope(object):\r
200 \r
201             def __init__(self,\r
202                          servicename="",\r
203                          scope=""):\r
204 \r
205                 self.servicename = servicename\r
206                 self.scope = scope\r
207 \r
208             def __repr__(self):\r
209                 s = "<GuaranteeScope(servicename={}, scope={})>"\r
210                 return s.format(\r
211                     self.servicename,\r
212                     self.scope\r
213                 )\r
214 \r
215         def __init__(self,\r
216                      metric_name="",\r
217                      bounds=(0, 0),\r
218                      guarantee_scopes=()):\r
219             """Creates a GuaranteeTerm.\r
220 \r
221             Take into account that the GT's name is based on the metric_name.\r
222             :param str metric_name: name of the service property being enforced\r
223             :param bounds: (lower, upper) bounds of the metric values\r
224             :type bounds: (float, float)\r
225             """\r
226             self.name = "GT_{}".format(metric_name)\r
227             self.metric_name = metric_name\r
228             self.kpiname = metric_name\r
229             self.bounds = bounds\r
230             self.guarantee_scopes = guarantee_scopes\r
231 \r
232         def __repr__(self):\r
233             s = "<GuaranteeTerm(name={}, metric_name={}, " \\r
234                 "kpiname={}, bounds={}, guarantee_scopes={})>"\r
235             return s.format(\r
236                 self.name,\r
237                 self.metric_name,\r
238                 self.kpiname,\r
239                 self.bounds,\r
240                 repr(self.guarantee_scopes)\r
241             )\r
242 \r
243     def __init__(self,\r
244                  agreement_id="",\r
245                  agreement_name="",\r
246                  service_id="",\r
247                  consumer="",\r
248                  provider="",\r
249                  template_id="",\r
250                  expiration_time=None,\r
251                  service_properties=(),\r
252                  guarantee_terms=()):\r
253         """Input data to the template for generating a sla-agreement\r
254 \r
255         :param str agreement_id: optional agreement id. If not supplied,\r
256             the SlaManager should create one.\r
257         :param str agreement_name: optional agreement name\r
258         :param str service_id: Domain id/name of the service.\r
259         :param str consumer: Id of the consumer party in the agreement.\r
260         :param str provider: Resource Id of the provider party in the agreement\r
261           The provider must exist previously in the SlaManager.\r
262         :param str template_id: TemplateId of the template this agreement is\r
263           based on.\r
264         :param expiration_time: Expiration time of this agreement.\r
265         :type expiration_time: datetime.datetime\r
266         :param service_properties: Should be the same of the template.\r
267         :type service_properties: list[slaclient.wsag_model.Agreement.Property]\r
268         :param guarantee_terms: Guarantee terms to be enforced in this\r
269           agreement.\r
270         :type guarantee_terms: list(AgreementInput.GuaranteeTerm)\r
271         """\r
272         self.agreement_id = agreement_id\r
273         self.agreement_name = agreement_name\r
274         self.service_id = service_id\r
275         self.consumer = consumer\r
276         self.provider = provider\r
277         self.template_id = template_id\r
278         self.expiration_time = expiration_time\r
279         self.expiration_time_iso = \\r
280             expiration_time if expiration_time else None\r
281         self.service_properties = service_properties\r
282         self.guarantee_terms = guarantee_terms\r
283 \r
284     def __repr__(self):\r
285         s = "<AgreementInput(agreement_id={}, agreement_name={}, " \\r
286             "service_id={}, consumer={}, provider={}, template_id={}, " \\r
287             "expiration_time={}, service_properties={}, guarantee_terms={}>"\r
288         return s.format(\r
289             self.agreement_id,\r
290             self.agreement_name,\r
291             self.service_id,\r
292             self.consumer,\r
293             self.provider,\r
294             self.template_id,\r
295             self.expiration_time,\r
296             repr(self.service_properties),\r
297             repr(self.guarantee_terms)\r
298         )\r
299 \r
300     def from_template(self, slatemplate):\r
301         """Return a new agreement based on this agreement and copying info\r
302         (overriding if necessary) from a slatemplate.\r
303 \r
304         :type slatemplate: wsag_model.Template\r
305         :rtype: AgreementInput\r
306         """\r
307         #\r
308         # NOTE: templateinput does not address guaranteeterms (yet)\r
309         #\r
310 \r
311         for _, gt in slatemplate.guaranteeterms.items():\r
312             gt.scopes[0].scope = self.guarantee_terms[0].guarantee_scopes.scope\r
313             gt.scopes[0].scope = [x.encode('utf-8') for x in gt.scopes[0].scope]\r
314 \r
315         result = AgreementInput(\r
316             agreement_id=self.agreement_id,\r
317             agreement_name=self.agreement_name,\r
318             service_id=slatemplate.context.service,\r
319             consumer=self.consumer,\r
320             provider=slatemplate.context.provider or self.provider,\r
321             template_id=slatemplate.template_id,\r
322             expiration_time=self.expiration_time,\r
323             service_properties=slatemplate.variables.values(),\r
324             #guarantee_terms=self.guarantee_terms\r
325             guarantee_terms=slatemplate.guaranteeterms.values()\r
326         )\r
327 \r
328         return result\r