blob: b52d48c4492345e394c239ed51bd503f34358407 [file] [log] [blame]
ZhiQiang Fan39f97222013-09-20 04:49:44 +08001# Copyright 2012 OpenStack Foundation
Attila Fazekasa23f5002012-10-23 19:32:45 +02002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
Attila Fazekas1aed6202013-02-11 14:47:45 +010016import ConfigParser
17import contextlib
Attila Fazekas1aed6202013-02-11 14:47:45 +010018import types
Attila Fazekasa23f5002012-10-23 19:32:45 +020019import urlparse
20
Matthew Treinish684d8992014-01-30 16:27:40 +000021from tempest import config
Attila Fazekas1aed6202013-02-11 14:47:45 +010022from tempest import exceptions
Matthew Treinisha83a16e2012-12-07 13:44:02 -050023
Attila Fazekas1aed6202013-02-11 14:47:45 +010024import boto
25import boto.ec2
26import boto.s3.connection
27
Matthew Treinish684d8992014-01-30 16:27:40 +000028CONF = config.CONF
29
Attila Fazekas1aed6202013-02-11 14:47:45 +010030
31class BotoClientBase(object):
32
33 ALLOWED_METHODS = set()
34
Matthew Treinish684d8992014-01-30 16:27:40 +000035 def __init__(self, username=None, password=None,
Attila Fazekas1aed6202013-02-11 14:47:45 +010036 auth_url=None, tenant_name=None,
37 *args, **kwargs):
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000038 # FIXME(andreaf) replace credentials and auth_url with auth_provider
Attila Fazekas1aed6202013-02-11 14:47:45 +010039
Matthew Treinish684d8992014-01-30 16:27:40 +000040 self.connection_timeout = str(CONF.boto.http_socket_timeout)
41 self.num_retries = str(CONF.boto.num_retries)
42 self.build_timeout = CONF.boto.build_timeout
Attila Fazekas1aed6202013-02-11 14:47:45 +010043 self.ks_cred = {"username": username,
44 "password": password,
45 "auth_url": auth_url,
46 "tenant_name": tenant_name}
47
48 def _keystone_aws_get(self):
Andrea Frittolif9cde7e2014-02-18 09:57:04 +000049 # FIXME(andreaf) Move EC2 credentials to AuthProvider
Attila Fazekas1aed6202013-02-11 14:47:45 +010050 import keystoneclient.v2_0.client
51
52 keystone = keystoneclient.v2_0.client.Client(**self.ks_cred)
53 ec2_cred_list = keystone.ec2.list(keystone.auth_user_id)
54 ec2_cred = None
55 for cred in ec2_cred_list:
56 if cred.tenant_id == keystone.auth_tenant_id:
57 ec2_cred = cred
58 break
59 else:
60 ec2_cred = keystone.ec2.create(keystone.auth_user_id,
61 keystone.auth_tenant_id)
62 if not all((ec2_cred, ec2_cred.access, ec2_cred.secret)):
63 raise exceptions.NotFound("Unable to get access and secret keys")
64 return ec2_cred
65
66 def _config_boto_timeout(self, timeout, retries):
67 try:
68 boto.config.add_section("Boto")
69 except ConfigParser.DuplicateSectionError:
70 pass
71 boto.config.set("Boto", "http_socket_timeout", timeout)
72 boto.config.set("Boto", "num_retries", retries)
73
74 def __getattr__(self, name):
75 """Automatically creates methods for the allowed methods set."""
76 if name in self.ALLOWED_METHODS:
77 def func(self, *args, **kwargs):
78 with contextlib.closing(self.get_connection()) as conn:
79 return getattr(conn, name)(*args, **kwargs)
80
81 func.__name__ = name
82 setattr(self, name, types.MethodType(func, self, self.__class__))
83 setattr(self.__class__, name,
84 types.MethodType(func, None, self.__class__))
85 return getattr(self, name)
86 else:
87 raise AttributeError(name)
88
89 def get_connection(self):
90 self._config_boto_timeout(self.connection_timeout, self.num_retries)
91 if not all((self.connection_data["aws_access_key_id"],
92 self.connection_data["aws_secret_access_key"])):
93 if all(self.ks_cred.itervalues()):
94 ec2_cred = self._keystone_aws_get()
95 self.connection_data["aws_access_key_id"] = \
96 ec2_cred.access
97 self.connection_data["aws_secret_access_key"] = \
98 ec2_cred.secret
99 else:
100 raise exceptions.InvalidConfiguration(
Sean Dague14c68182013-04-14 15:34:30 -0400101 "Unable to get access and secret keys")
Attila Fazekas1aed6202013-02-11 14:47:45 +0100102 return self.connect_method(**self.connection_data)
Matthew Treinisha83a16e2012-12-07 13:44:02 -0500103
Attila Fazekasa23f5002012-10-23 19:32:45 +0200104
105class APIClientEC2(BotoClientBase):
106
107 def connect_method(self, *args, **kwargs):
108 return boto.connect_ec2(*args, **kwargs)
109
Matthew Treinish684d8992014-01-30 16:27:40 +0000110 def __init__(self, *args, **kwargs):
111 super(APIClientEC2, self).__init__(*args, **kwargs)
112 aws_access = CONF.boto.aws_access
113 aws_secret = CONF.boto.aws_secret
114 purl = urlparse.urlparse(CONF.boto.ec2_url)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200115
Matthew Treinish684d8992014-01-30 16:27:40 +0000116 region_name = CONF.compute.region
Arata Notsu8f440392013-09-13 16:14:20 +0900117 if not region_name:
Matthew Treinish684d8992014-01-30 16:27:40 +0000118 region_name = CONF.identity.region
Arata Notsu8f440392013-09-13 16:14:20 +0900119 region = boto.ec2.regioninfo.RegionInfo(name=region_name,
Attila Fazekas1aed6202013-02-11 14:47:45 +0100120 endpoint=purl.hostname)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200121 port = purl.port
122 if port is None:
123 if purl.scheme is not "https":
124 port = 80
125 else:
126 port = 443
127 else:
128 port = int(port)
129 self.connection_data = {"aws_access_key_id": aws_access,
130 "aws_secret_access_key": aws_secret,
131 "is_secure": purl.scheme == "https",
132 "region": region,
133 "host": purl.hostname,
134 "port": port,
135 "path": purl.path}
136
137 ALLOWED_METHODS = set(('create_key_pair', 'get_key_pair',
138 'delete_key_pair', 'import_key_pair',
139 'get_all_key_pairs',
Burt Holzmanbb575e12013-07-14 00:23:30 -0500140 'get_all_tags',
Attila Fazekasa23f5002012-10-23 19:32:45 +0200141 'create_image', 'get_image',
142 'register_image', 'deregister_image',
143 'get_all_images', 'get_image_attribute',
144 'modify_image_attribute', 'reset_image_attribute',
145 'get_all_kernels',
146 'create_volume', 'delete_volume',
147 'get_all_volume_status', 'get_all_volumes',
148 'get_volume_attribute', 'modify_volume_attribute'
149 'bundle_instance', 'cancel_spot_instance_requests',
150 'confirm_product_instanc',
151 'get_all_instance_status', 'get_all_instances',
152 'get_all_reserved_instances',
153 'get_all_spot_instance_requests',
154 'get_instance_attribute', 'monitor_instance',
155 'monitor_instances', 'unmonitor_instance',
156 'unmonitor_instances',
157 'purchase_reserved_instance_offering',
158 'reboot_instances', 'request_spot_instances',
159 'reset_instance_attribute', 'run_instances',
160 'start_instances', 'stop_instances',
161 'terminate_instances',
162 'attach_network_interface', 'attach_volume',
163 'detach_network_interface', 'detach_volume',
164 'get_console_output',
165 'delete_network_interface', 'create_subnet',
166 'create_network_interface', 'delete_subnet',
167 'get_all_network_interfaces',
168 'allocate_address', 'associate_address',
169 'disassociate_address', 'get_all_addresses',
170 'release_address',
171 'create_snapshot', 'delete_snapshot',
172 'get_all_snapshots', 'get_snapshot_attribute',
173 'modify_snapshot_attribute',
174 'reset_snapshot_attribute', 'trim_snapshots',
175 'get_all_regions', 'get_all_zones',
176 'get_all_security_groups', 'create_security_group',
177 'delete_security_group', 'authorize_security_group',
178 'authorize_security_group_egress',
179 'revoke_security_group',
180 'revoke_security_group_egress'))
181
182 def get_good_zone(self):
183 """
184 :rtype: BaseString
185 :return: Returns with the first available zone name
186 """
187 for zone in self.get_all_zones():
Attila Fazekasa8b5fe72013-08-01 16:59:06 +0200188 # NOTE(afazekas): zone.region_name was None
Attila Fazekasa23f5002012-10-23 19:32:45 +0200189 if (zone.state == "available" and
190 zone.region.name == self.connection_data["region"].name):
191 return zone.name
192 else:
193 raise IndexError("Don't have a good zone")
194
195
196class ObjectClientS3(BotoClientBase):
197
198 def connect_method(self, *args, **kwargs):
199 return boto.connect_s3(*args, **kwargs)
200
Matthew Treinish684d8992014-01-30 16:27:40 +0000201 def __init__(self, *args, **kwargs):
202 super(ObjectClientS3, self).__init__(*args, **kwargs)
203 aws_access = CONF.boto.aws_access
204 aws_secret = CONF.boto.aws_secret
205 purl = urlparse.urlparse(CONF.boto.s3_url)
Attila Fazekasa23f5002012-10-23 19:32:45 +0200206 port = purl.port
207 if port is None:
208 if purl.scheme is not "https":
209 port = 80
210 else:
211 port = 443
212 else:
213 port = int(port)
214 self.connection_data = {"aws_access_key_id": aws_access,
215 "aws_secret_access_key": aws_secret,
216 "is_secure": purl.scheme == "https",
217 "host": purl.hostname,
218 "port": port,
Attila Fazekas1aed6202013-02-11 14:47:45 +0100219 "calling_format": boto.s3.connection.
220 OrdinaryCallingFormat()}
Attila Fazekasa23f5002012-10-23 19:32:45 +0200221
222 ALLOWED_METHODS = set(('create_bucket', 'delete_bucket', 'generate_url',
223 'get_all_buckets', 'get_bucket', 'delete_key',
224 'lookup'))