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