summaryrefslogtreecommitdiffstats
path: root/azure/aria/aria-extension-cloudify/src/aria/aria/core.py
blob: e364f48f303639b049355123b2024395f2bbbd93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
ARIA core module.
"""

from . import exceptions
from .parser import consumption
from .parser.loading.location import UriLocation
from .orchestrator import topology


class Core(object):

    def __init__(self,
                 model_storage,
                 resource_storage,
                 plugin_manager):
        self._model_storage = model_storage
        self._resource_storage = resource_storage
        self._plugin_manager = plugin_manager

    @property
    def model_storage(self):
        return self._model_storage

    @property
    def resource_storage(self):
        return self._resource_storage

    @property
    def plugin_manager(self):
        return self._plugin_manager

    def validate_service_template(self, service_template_path):
        self._parse_service_template(service_template_path)

    def create_service_template(self, service_template_path, service_template_dir,
                                service_template_name):
        context = self._parse_service_template(service_template_path)
        service_template = context.modeling.template
        service_template.name = service_template_name
        self.model_storage.service_template.put(service_template)
        self.resource_storage.service_template.upload(
            entry_id=str(service_template.id), source=service_template_dir)
        return service_template.id

    def delete_service_template(self, service_template_id):
        service_template = self.model_storage.service_template.get(service_template_id)
        if service_template.services:
            raise exceptions.DependentServicesError(
                'Can\'t delete service template `{0}` - service template has existing services'
                .format(service_template.name))

        self.model_storage.service_template.delete(service_template)
        self.resource_storage.service_template.delete(entry_id=str(service_template.id))

    def create_service(self, service_template_id, inputs, service_name=None):
        service_template = self.model_storage.service_template.get(service_template_id)

        storage_session = self.model_storage._all_api_kwargs['session']
        # setting no autoflush for the duration of instantiation - this helps avoid dependency
        # constraints as they're being set up
        with storage_session.no_autoflush:
            topology_ = topology.Topology()
            service = topology_.instantiate(
                service_template, inputs=inputs, plugins=self.model_storage.plugin.list())
            topology_.coerce(service, report_issues=True)

            topology_.validate(service)
            topology_.satisfy_requirements(service)
            topology_.coerce(service, report_issues=True)

            topology_.validate_capabilities(service)
            topology_.assign_hosts(service)
            topology_.configure_operations(service)
            topology_.coerce(service, report_issues=True)
            if topology_.dump_issues():
                raise exceptions.InstantiationError('Failed to instantiate service template `{0}`'
                                                    .format(service_template.name))

        storage_session.flush()  # flushing so service.id would auto-populate
        service.name = service_name or '{0}_{1}'.format(service_template.name, service.id)
        self.model_storage.service.put(service)
        return service

    def delete_service(self, service_id, force=False):
        service = self.model_storage.service.get(service_id)

        active_executions = [e for e in service.executions if e.is_active()]
        if active_executions:
            raise exceptions.DependentActiveExecutionsError(
                'Can\'t delete service `{0}` - there is an active execution for this service. '
                'Active execution ID: {1}'.format(service.name, active_executions[0].id))

        if not force:
            available_nodes = [str(n.id) for n in service.nodes.itervalues() if n.is_available()]
            if available_nodes:
                raise exceptions.DependentAvailableNodesError(
                    'Can\'t delete service `{0}` - there are available nodes for this service. '
                    'Available node IDs: {1}'.format(service.name, ', '.join(available_nodes)))

        self.model_storage.service.delete(service)

    @staticmethod
    def _parse_service_template(service_template_path):
        context = consumption.ConsumptionContext()
        context.presentation.location = UriLocation(service_template_path)
        # Most of the parser uses the topology package in order to manipulate the models.
        # However, here we use the Consumer mechanism, but this should change in the future.
        consumption.ConsumerChain(
            context,
            (
                consumption.Read,
                consumption.Validate,
                consumption.ServiceTemplate
            )).consume()
        if context.validation.dump_issues():
            raise exceptions.ParsingError('Failed to parse service template')
        return context