Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / commissioning / callpoint.py @ 2005b18e

History | View | Annotate | Download (7.8 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34

    
35
from kamaki.clients.commissioning.specificator import CanonifyException
36
from kamaki.clients.commissioning.exception import CorruptedError
37
from kamaki.clients.commissioning.exception import InvalidDataError
38
from kamaki.clients.commissioning.exception import ReturnButFail
39
from kamaki.clients.commissioning.importing import imp_module
40

    
41
from re import compile as re_compile, sub as re_sub
42

    
43

    
44
class Callpoint(object):
45

    
46
    api_spec = None
47

    
48
    CorruptedError = CorruptedError
49
    InvalidDataError = InvalidDataError
50

    
51
    original_calls = None
52

    
53
    def __init__(self, connection=None):
54
        from json import loads, dumps
55

    
56
        self.json_loads = loads
57
        self.json_dumps = dumps
58
        self.init_connection(connection)
59
        original_calls = {}
60
        self.original_calls = original_calls
61
        canonifier = self.api_spec
62

    
63
        if canonifier is None:
64
            m = "No api spec given to '%s'" % (type(self).__name__,)
65
            raise NotImplementedError(m)
66

    
67
        for call_name, call_doc in canonifier.call_docs():
68
            if hasattr(self, call_name):
69

    
70
                #raise ValueError(m)
71
                call_func = getattr(self, call_name)
72
                if not callable(call_func):
73
                    raise ValueError(' '.join([
74
                        "api spec '%s'," % type(canonifier).__name__,
75
                        "method '%s' is not a callable" % call_name,
76
                        "attribute in callpoint '%s'" % type(self).__name]))
77

    
78
                original_calls[call_name] = call_func
79

    
80
            def mk_call_func():
81
                local_call_name = call_name
82

    
83
                def call_func(**data):
84
                    return self.make_call(local_call_name, data)
85

    
86
                call_func.__name__ = call_name
87
                call_func.__doc__ = call_doc
88
                return call_func
89

    
90
            setattr(self, call_name, mk_call_func())
91

    
92
    def init_connection(self, connection):
93
        pass
94

    
95
    def commit(self):
96
        pass
97

    
98
    def rollback(self):
99
        pass
100

    
101
    def do_make_call(self, call_name, data):
102
        raise NotImplementedError
103

    
104
    def validate_call(self, call_name):
105
        return hasattr(self, call_name)
106

    
107
    def make_call_from_json_description(self, json_description):
108
        try:
109
            description = self.json_loads(json_description)
110
        except ValueError:
111
            m = "Cannot load json description"
112
            raise self.InvalidDataError(m)
113

    
114
        data = self.make_call_from_description(description)
115
        json_data = self.json_dumps(data) if data is not None else None
116
        return json_data
117

    
118
    def make_call_from_description(self, description):
119
        try:
120
            call_name = description['call_name']
121
            call_data = description['call_data']
122
        except (TypeError, KeyError), e:
123
            m = "Invalid description"
124
            raise self.InvalidDataError(m, e)
125

    
126
        return self.make_call(call_name, call_data)
127

    
128
    def make_call_from_json(self, call_name, json_data):
129
        if json_data:
130
            try:
131
                data = self.json_loads(json_data)
132
            except ValueError, e:
133
                m = "Cannot load json data"
134
                raise self.InvalidDataError(m, e)
135
        else:
136
            data = None
137

    
138
        data = self.make_call(call_name, data)
139
        json_data = self.json_dumps(data)
140
        return json_data
141

    
142
    def make_call(self, call_name, data):
143
        if call_name.startswith('_'):
144
            m = "Invalid call '%s'" % (call_name,)
145
            raise self.InvalidDataError(m)
146

    
147
        canonifier = self.api_spec
148
        try:
149
            data = canonifier.canonify_input(call_name, data)
150
        except CanonifyException, e:
151
            m = "Invalid input to call '%s'" % (call_name,)
152
            raise self.InvalidDataError(m, e)
153

    
154
        if not self.validate_call(call_name):
155
            m = "Cannot find specified call '%s'" % (call_name,)
156
            raise self.CorruptedError(m)
157

    
158
        call_func = self.original_calls.get(call_name, None)
159
        try:
160
            if call_func is None:
161
                data = self.do_make_call(call_name, data)
162
            else:
163
                data = call_func(**data)
164
            self.commit()
165
        except ReturnButFail, e:
166
            self.rollback()
167
            data = e.data
168
        except Exception, e:
169
            self.rollback()
170
            raise
171

    
172
        try:
173
            data = canonifier.canonify_output(call_name, data)
174
        except CanonifyException, e:
175
            m = "Invalid output from call '%s'" % (call_name,)
176
            raise self.CorruptedError(m, e)
177

    
178
        return data
179

    
180

    
181
def mkcallargs(**kw):
182
    return kw
183

    
184

    
185
versiontag_pattern = re_compile('[^a-zA-Z0-9_-]')
186

    
187

    
188
def mk_versiontag(version):
189
    if not version or version == 'v':
190
        return ''
191

    
192
    return '_' + re_sub(versiontag_pattern, '_', version)
193

    
194

    
195
def get_callpoint(pointname, version=None, automake=None, **kw):
196

    
197
    versiontag = mk_versiontag(version)
198
    components = pointname.split('.')
199

    
200
    appname = components[0]
201
    if len(components) < 2:
202
        raise ValueError("invalid pointname '%s'" % (pointname,))
203

    
204
    category = components[1]
205
    if not category or category not in ['clients', 'servers']:
206
        raise ValueError("invalid pointname '%s'" % (pointname,))
207

    
208
    modname = ('%s.callpoint.API_Callpoint%s' % (pointname, versiontag))
209

    
210
    try:
211
        API_Callpoint = imp_module(modname)
212
        return API_Callpoint
213
    except ImportError:
214
        if not automake:
215
            raise
216

    
217
    if category != 'clients':
218
        m = (
219
            "Can only auto-make callpoint in 'clients' not '%s'" % (category))
220
        raise ValueError(m)
221

    
222
    components = components[1:]
223
    if not components:
224
        raise ValueError("invalid pointname '%s'" % (pointname))
225

    
226
    pointname = '.'.join(components)
227
    if pointname == 'quotaholder':
228
        apiname = 'quotaholder.api.QuotaholderAPI'
229
    else:
230
        apiname = '%s.api.API_Spec%s' % (pointname, versiontag)
231

    
232
    API_Spec = imp_module(apiname)
233

    
234
    basename = 'commissioning.clients.%s.API_Callpoint' % (automake,)
235
    BaseCallpoint = imp_module(basename)
236

    
237
    stupidpython = (appname,
238
                    version if version is not None else 'v',
239
                    pointname,
240
                    automake)
241

    
242
    class AutoCallpoint(BaseCallpoint):
243
        appname, version, pointname, automake = stupidpython
244
        api_spec = API_Spec()
245

    
246
    return AutoCallpoint