d3aa860eab06b7b75c3eabb686da19fdf24e2152
[osm/SO.git] / common / python / rift / mano / cloud / accounts.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
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 import sys
19 import asyncio
20 from gi import require_version
21 require_version('RwcalYang', '1.0')
22 require_version('RwTypes', '1.0')
23 require_version('RwCloudYang', '1.0')
24 require_version('RwCal', '1.0')
25
26 from gi.repository import (
27 RwTypes,
28 RwcalYang,
29 RwCloudYang,
30 )
31 import rw_peas
32
33 if sys.version_info < (3, 4, 4):
34 asyncio.ensure_future = asyncio.async
35
36
37 class PluginLoadingError(Exception):
38 pass
39
40
41 class CloudAccountCalError(Exception):
42 pass
43
44
45 class CloudAccount(object):
46 def __init__(self, log, rwlog_hdl, account_msg):
47 self._log = log
48 self._account_msg = account_msg.deep_copy()
49
50 self._cal_plugin = None
51 self._engine = None
52
53 self._cal = self.plugin.get_interface("Cloud")
54 self._cal.init(rwlog_hdl)
55
56 self._status = RwCloudYang.CloudAccount_ConnectionStatus(
57 status="unknown",
58 details="Connection status lookup not started"
59 )
60
61 self._validate_task = None
62
63 @property
64 def plugin(self):
65 if self._cal_plugin is None:
66 try:
67 self._cal_plugin = rw_peas.PeasPlugin(
68 getattr(self._account_msg, self.account_type).plugin_name,
69 'RwCal-1.0',
70 )
71
72 except AttributeError as e:
73 raise PluginLoadingError(str(e))
74
75 self._engine, _, _ = self._cal_plugin()
76
77 return self._cal_plugin
78
79 def _wrap_status_fn(self, fn, *args, **kwargs):
80 ret = fn(*args, **kwargs)
81 rw_status = ret[0]
82 if rw_status != RwTypes.RwStatus.SUCCESS:
83 msg = "%s returned %s" % (fn.__name__, str(rw_status))
84 self._log.error(msg)
85 raise CloudAccountCalError(msg)
86
87 # If there was only one other return value besides rw_status, then just
88 # return that element. Otherwise return the rest of the return values
89 # as a list.
90 return ret[1] if len(ret) == 2 else ret[1:]
91
92 @property
93 def cal(self):
94 return self._cal
95
96 @property
97 def name(self):
98 return self._account_msg.name
99
100 @property
101 def account_msg(self):
102 return self._account_msg
103
104 @property
105 def cal_account_msg(self):
106 return RwcalYang.CloudAccount.from_dict(
107 self.account_msg.as_dict(),
108 ignore_missing_keys=True,
109 )
110
111 def cloud_account_msg(self, account_dict):
112 self._account_msg = RwCloudYang.CloudAccount.from_dict(account_dict)
113
114 @property
115 def account_type(self):
116 return self._account_msg.account_type
117
118 @property
119 def connection_status(self):
120 return self._status
121
122 def update_from_cfg(self, cfg):
123 self._log.debug("Updating parent CloudAccount to %s", cfg)
124
125 # Hack to catch updates triggered from apply_callback when a sdn-account is removed
126 # from a cloud-account. To be fixed properly when updates are handled
127 if (self.account_msg.name == cfg.name
128 and self.account_msg.account_type == cfg.account_type):
129 return
130
131 if cfg.has_field("sdn_account"):
132 self.account_msg.sdn_account = cfg.sdn_account
133 else:
134 raise NotImplementedError("Update cloud account not yet supported")
135
136 def create_image(self, image_info_msg):
137 image_id = self._wrap_status_fn(
138 self.cal.create_image, self.cal_account_msg, image_info_msg
139 )
140
141 return image_id
142
143 def get_image_list(self):
144 self._log.debug("Getting image list from account: %s", self.name)
145 resources = self._wrap_status_fn(
146 self.cal.get_image_list, self.cal_account_msg
147 )
148
149 return resources.imageinfo_list
150
151 @asyncio.coroutine
152 def validate_cloud_account_credentials(self, loop):
153 self._log.debug("Validating Cloud Account credentials %s", self._account_msg)
154 self._status = RwCloudYang.CloudAccount_ConnectionStatus(
155 status="validating",
156 details="Cloud account connection validation in progress"
157 )
158 rwstatus, status = yield from loop.run_in_executor(
159 None,
160 self._cal.validate_cloud_creds,
161 self.cal_account_msg,
162 )
163 if rwstatus == RwTypes.RwStatus.SUCCESS:
164 self._status = RwCloudYang.CloudAccount_ConnectionStatus.from_dict(status.as_dict())
165 else:
166 self._status = RwCloudYang.CloudAccount_ConnectionStatus(
167 status="failure",
168 details="Error when calling CAL validate cloud creds"
169 )
170
171 self._log.info("Got cloud account validation response: %s", self._status)
172
173 def start_validate_credentials(self, loop):
174 if self._validate_task is not None:
175 self._validate_task.cancel()
176 self._validate_task = None
177
178 self._validate_task = asyncio.ensure_future(
179 self.validate_cloud_account_credentials(loop),
180 loop=loop
181 )
182