Convert check_guestfs_version into an Image method
[snf-image-creator] / image_creator / kamaki_wrapper.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2012 GRNET S.A. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or
6 # without modification, are permitted provided that the following
7 # conditions are met:
8 #
9 #   1. Redistributions of source code must retain the above
10 #      copyright notice, this list of conditions and the following
11 #      disclaimer.
12 #
13 #   2. Redistributions in binary form must reproduce the above
14 #      copyright notice, this list of conditions and the following
15 #      disclaimer in the documentation and/or other materials
16 #      provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
30 #
31 # The views and conclusions contained in the software and
32 # documentation are those of the authors and should not be
33 # interpreted as representing official policies, either expressed
34 # or implied, of GRNET S.A.
35
36 """This modules provides the interface for working with the ./kamaki library.
37 The library is used to upload images to and register them with a Synnefo
38 deployment.
39 """
40
41 from os.path import basename
42
43 from kamaki.cli.config import Config
44 from kamaki.clients import ClientError
45 from kamaki.clients.image import ImageClient
46 from kamaki.clients.pithos import PithosClient
47 from kamaki.clients.astakos import AstakosClient
48
49
50 config = Config()
51
52
53 class Kamaki(object):
54     """Wrapper class for the ./kamaki library"""
55     CONTAINER = "images"
56
57     @staticmethod
58     def get_default_cloud_name():
59         """Returns the name of the default cloud"""
60         clouds = config.keys('cloud')
61         default = config.get('global', 'default_cloud')
62         if not default:
63             return clouds[0] if len(clouds) else ""
64         return default if default in clouds else ""
65
66     @staticmethod
67     def set_default_cloud(name):
68         """Sets a cloud account as default"""
69         config.set('global', 'default_cloud', name)
70         config.write()
71
72     @staticmethod
73     def get_clouds():
74         """Returns the list of available clouds"""
75         names = config.keys('cloud')
76
77         clouds = {}
78         for name in names:
79             clouds[name] = config.get('cloud', name)
80
81         return clouds
82
83     @staticmethod
84     def get_cloud_by_name(name):
85         """Returns a dict with cloud info"""
86         return config.get('cloud', name)
87
88     @staticmethod
89     def save_cloud(name, url, token, description=""):
90         """Save a new cloud account"""
91         cloud = {'url': url, 'token': token}
92         if len(description):
93             cloud['description'] = description
94         config.set('cloud', name, cloud)
95
96         # Make the saved cloud the default one
97         config.set('global', 'default_cloud', name)
98         config.write()
99
100     @staticmethod
101     def remove_cloud(name):
102         """Deletes an existing cloud from the Kamaki configuration file"""
103         config.remove_option('cloud', name)
104         config.write()
105
106     @staticmethod
107     def create_account(url, token):
108         """Given a valid (URL, tokens) pair this method returns an Astakos
109         client instance
110         """
111         client = AstakosClient(url, token)
112         try:
113             client.authenticate()
114         except ClientError:
115             return None
116
117         return client
118
119     @staticmethod
120     def get_account(cloud_name):
121         """Given a saved cloud name this method returns an Astakos client
122         instance
123         """
124         cloud = config.get('cloud', cloud_name)
125         assert cloud, "cloud: `%s' does not exist" % cloud_name
126         assert 'url' in cloud, "url attr is missing in %s" % cloud_name
127         assert 'token' in cloud, "token attr is missing in %s" % cloud_name
128
129         return Kamaki.create_account(cloud['url'], cloud['token'])
130
131     def __init__(self, account, output):
132         """Create a Kamaki instance"""
133         self.account = account
134         self.out = output
135
136         self.pithos = PithosClient(
137             self.account.get_service_endpoints('object-store')['publicURL'],
138             self.account.token,
139             self.account.user_info()['id'],
140             self.CONTAINER)
141
142         self.image = ImageClient(
143             self.account.get_service_endpoints('image')['publicURL'],
144             self.account.token)
145
146     def upload(self, file_obj, size=None, remote_path=None, hp=None, up=None):
147         """Upload a file to pithos"""
148
149         path = basename(file_obj.name) if remote_path is None else remote_path
150
151         try:
152             self.pithos.create_container(self.CONTAINER)
153         except ClientError as e:
154             if e.status != 202:  # Ignore container already exists errors
155                 raise e
156
157         hash_cb = self.out.progress_generator(hp) if hp is not None else None
158         upload_cb = self.out.progress_generator(up) if up is not None else None
159
160         self.pithos.upload_object(path, file_obj, size, hash_cb, upload_cb)
161
162         return "pithos://%s/%s/%s" % (self.account.user_info()['id'],
163                                       self.CONTAINER, path)
164
165     def register(self, name, location, metadata, public=False):
166         """Register an image with cyclades"""
167
168         # Convert all metadata to strings
169         str_metadata = {}
170         for (key, value) in metadata.iteritems():
171             str_metadata[str(key)] = str(value)
172         is_public = 'true' if public else 'false'
173         params = {'is_public': is_public, 'disk_format': 'diskdump'}
174         return self.image.register(name, location, params, str_metadata)
175
176     def share(self, location):
177         """Share this file with all the users"""
178
179         self.pithos.set_object_sharing(location, "*")
180
181     def object_exists(self, location):
182         """Check if an object exists in pythos"""
183
184         try:
185             self.pithos.get_object_info(location)
186         except ClientError as e:
187             if e.status == 404:  # Object not found error
188                 return False
189             else:
190                 raise
191         return True
192
193 # vim: set sta sts=4 shiftwidth=4 sw=4 et ai :