1 # -*- coding: utf-8 -*-
\r
2 """Template system for fed4fire project.
\r
4 The specific template system is configured with the factory module variable.
\r
6 By default, it is set to use django.
\r
8 Each implementation must define a factory module/object, defining:
\r
12 that returns a slaclient.templates.Template-compliant object that performs
\r
15 This module defines two facade methods:
\r
16 * render_slaagreement(data)
\r
17 * render_slatemplate(data)
\r
19 and the corresponding input classes:
\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
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
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
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
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
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
60 <wsag:Variable Name="Uptime" Metric="xs:double">
\r
61 <wsag:Location>service-ping/Uptime</wsag:Location>
\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
71 In summary, in fed4fire, the service properties will hold the metrics being
\r
72 monitored for a service.
\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
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
83 <wsag:KPIName>Uptime</wsag:KPIName>
\r
84 <wsag:CustomServiceLevel>
\r
85 {"constraint" : "Uptime BETWEEN (90, 100)"}
\r
86 </wsag:CustomServiceLevel>
\r
88 </wsag:ServiceLevelObjective>
\r
89 </wsag:GuaranteeTerm>
\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
103 The CustomServiceLevel is not specified by ws-agreement, so it's something
\r
104 to be defined by the implementation.
\r
108 from sla.slaclient import wsag_model
\r
111 from sla.slaclient.templates.fed4fire.django.factory import Factory
\r
112 factory = Factory()
\r
117 # Hardwired above to avoid multheading issues. This will need some
\r
118 # refactoring if the factory really needs to be configurable.
\r
122 #if factory is None:
\r
123 # from slaclient.templates.fed4fire.django.factory import Factory
\r
124 # factory = Factory()
\r
128 def render_slaagreement(data):
\r
129 """Generate a sla agreement based on the supplied data.
\r
131 :type data: AgreementInput
\r
133 print "render_slaagreement"
\r
134 template = _getfactory().slaagreement()
\r
136 rendered = template.render(data)
\r
140 def render_slatemplate(data):
\r
141 """Generate a sla template based on the supplied data.
\r
143 :type data: TemplateInput
\r
145 template = _getfactory().slatemplate()
\r
146 return template.render(data)
\r
149 class TemplateInput(object):
\r
156 expiration_time=None,
\r
157 service_properties=()):
\r
158 """Input data to the template for generating a sla-template.
\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
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
181 def __repr__(self):
\r
182 s = "<TemplateInput(template_id={}, template_name={})" \
\r
183 "service_id={}, provider={}, expiration_time={}, " \
\r
184 "service_properties={}>"
\r
187 self.template_name,
\r
190 self.expiration_time_iso,
\r
191 repr(self.service_properties)
\r
195 class AgreementInput(object):
\r
197 class GuaranteeTerm(object):
\r
202 """Creates a GuaranteeTerm.
\r
204 Take into account that the GT's name is based on the metric_name.
\r
205 :param str metric_name: name of the service property being enforced.
\r
206 :param bounds: (lower, upper) bounds of the metric values.
\r
207 :type bounds: (float, float)
\r
209 self.name = "GT_{}".format(metric_name)
\r
210 self.metric_name = metric_name
\r
211 self.kpiname = metric_name
\r
212 self.bounds = bounds
\r
221 expiration_time=None,
\r
222 service_properties=(),
\r
223 guarantee_terms=()):
\r
224 """Input data to the template for generating a sla-agreement
\r
226 :param str agreement_id: optional agreement id. If not supplied,
\r
227 the SlaManager should create one.
\r
228 :param str agreement_name: optional agreement name
\r
229 :param str service_id: Domain id/name of the service.
\r
230 :param str consumer: Id of the consumer party in the agreement.
\r
231 :param str provider: Resource Id of the provider party in the agreement.
\r
232 The provider must exist previously in the SlaManager.
\r
233 :param str template_id: TemplateId of the template this agreement is
\r
235 :param expiration_time: Expiration time of this agreement.
\r
236 :type expiration_time: datetime.datetime
\r
237 :param service_properties: Should be the same of the template.
\r
238 :type service_properties: list[slaclient.wsag_model.Agreement.Property]
\r
239 :param guarantee_terms: Guarantee terms to be enforced in this
\r
241 :type guarantee_terms: list(AgreementInput.GuaranteeTerm)
\r
243 self.agreement_id = agreement_id
\r
244 self.agreement_name = agreement_name
\r
245 self.service_id = service_id
\r
246 self.consumer = consumer
\r
247 self.provider = provider
\r
248 self.template_id = template_id
\r
249 self.expiration_time = expiration_time
\r
250 self.expiration_time_iso = \
\r
251 expiration_time if expiration_time else None
\r
252 self.service_properties = service_properties
\r
253 self.guarantee_terms = guarantee_terms
\r
255 def __repr__(self):
\r
256 s = "<AgreementInput(agreement_id={}, agreement_name={}, " \
\r
257 "service_id={}, consumer={}, provider={}, template_id={}, " \
\r
258 "expiration_time={}, service_properties={}, guarantee_terms={}>"
\r
261 self.agreement_name,
\r
266 self.expiration_time,
\r
267 repr(self.service_properties),
\r
268 repr(self.guarantee_terms)
\r
271 def from_template(self, slatemplate):
\r
272 """Return a new agreement based on this agreement and copying info
\r
273 (overriding if necessary) from a slatemplate.
\r
275 :type slatemplate: wsag_model.Template
\r
276 :rtype: AgreementInput
\r
279 # NOTE: templateinput does not address guaranteeterms (yet)
\r
281 result = AgreementInput(
\r
282 agreement_id=self.agreement_id,
\r
283 agreement_name=self.agreement_name,
\r
284 service_id=slatemplate.context.service,
\r
285 consumer=self.consumer,
\r
286 provider=slatemplate.context.provider or self.provider,
\r
287 template_id=slatemplate.template_id,
\r
288 expiration_time=self.expiration_time,
\r
289 service_properties=slatemplate.variables.values(),
\r
290 guarantee_terms=slatemplate.guaranteeterms.values()
\r
292 print result.guarantee_terms[0]
\r