Statistics
| Branch: | Tag: | Revision:

root / image_creator / kamaki_wrapper.py @ 0014ab89

History | View | Annotate | Download (6.6 kB)

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
import sys
42

    
43
from os.path import basename
44

    
45
from kamaki.cli.config import Config
46
from kamaki.clients import ClientError
47
from kamaki.clients.image import ImageClient
48
from kamaki.clients.pithos import PithosClient
49
from kamaki.clients.astakos import CachedAstakosClient as AstakosClient
50

    
51
try:
52
    config = Config()
53
except Exception as e:
54
    sys.stderr.write("Kamaki config error: %s\n" % str(e))
55
    sys.exit(1)
56

    
57

    
58
class Kamaki(object):
59
    """Wrapper class for the ./kamaki library"""
60
    CONTAINER = "images"
61

    
62
    @staticmethod
63
    def get_default_cloud_name():
64
        """Returns the name of the default cloud"""
65
        clouds = config.keys('cloud')
66
        default = config.get('global', 'default_cloud')
67
        if not default:
68
            return clouds[0] if len(clouds) else ""
69
        return default if default in clouds else ""
70

    
71
    @staticmethod
72
    def set_default_cloud(name):
73
        """Sets a cloud account as default"""
74
        config.set('global', 'default_cloud', name)
75
        config.write()
76

    
77
    @staticmethod
78
    def get_clouds():
79
        """Returns the list of available clouds"""
80
        names = config.keys('cloud')
81

    
82
        clouds = {}
83
        for name in names:
84
            clouds[name] = config.get('cloud', name)
85

    
86
        return clouds
87

    
88
    @staticmethod
89
    def get_cloud_by_name(name):
90
        """Returns a dict with cloud info"""
91
        return config.get('cloud', name)
92

    
93
    @staticmethod
94
    def save_cloud(name, url, token, description=""):
95
        """Save a new cloud account"""
96
        cloud = {'url': url, 'token': token}
97
        if len(description):
98
            cloud['description'] = description
99
        config.set('cloud', name, cloud)
100

    
101
        # Make the saved cloud the default one
102
        config.set('global', 'default_cloud', name)
103
        config.write()
104

    
105
    @staticmethod
106
    def remove_cloud(name):
107
        """Deletes an existing cloud from the Kamaki configuration file"""
108
        config.remove_option('cloud', name)
109
        config.write()
110

    
111
    @staticmethod
112
    def create_account(url, token):
113
        """Given a valid (URL, tokens) pair this method returns an Astakos
114
        client instance
115
        """
116
        client = AstakosClient(url, token)
117
        try:
118
            client.authenticate()
119
        except ClientError:
120
            return None
121

    
122
        return client
123

    
124
    @staticmethod
125
    def get_account(cloud_name):
126
        """Given a saved cloud name this method returns an Astakos client
127
        instance
128
        """
129
        cloud = config.get('cloud', cloud_name)
130
        assert cloud, "cloud: `%s' does not exist" % cloud_name
131
        assert 'url' in cloud, "url attr is missing in %s" % cloud_name
132
        assert 'token' in cloud, "token attr is missing in %s" % cloud_name
133

    
134
        return Kamaki.create_account(cloud['url'], cloud['token'])
135

    
136
    def __init__(self, account, output):
137
        """Create a Kamaki instance"""
138
        self.account = account
139
        self.out = output
140

    
141
        self.pithos = PithosClient(
142
            self.account.get_service_endpoints('object-store')['publicURL'],
143
            self.account.token,
144
            self.account.user_info()['id'],
145
            self.CONTAINER)
146

    
147
        self.image = ImageClient(
148
            self.account.get_service_endpoints('image')['publicURL'],
149
            self.account.token)
150

    
151
    def upload(self, file_obj, size=None, remote_path=None, hp=None, up=None):
152
        """Upload a file to pithos"""
153

    
154
        path = basename(file_obj.name) if remote_path is None else remote_path
155

    
156
        try:
157
            self.pithos.create_container(self.CONTAINER)
158
        except ClientError as e:
159
            if e.status != 202:  # Ignore container already exists errors
160
                raise e
161

    
162
        hash_cb = self.out.progress_generator(hp) if hp is not None else None
163
        upload_cb = self.out.progress_generator(up) if up is not None else None
164

    
165
        self.pithos.upload_object(path, file_obj, size, hash_cb, upload_cb)
166

    
167
        return "pithos://%s/%s/%s" % (self.account.user_info()['id'],
168
                                      self.CONTAINER, path)
169

    
170
    def register(self, name, location, metadata, public=False):
171
        """Register an image with cyclades"""
172

    
173
        # Convert all metadata to strings
174
        str_metadata = {}
175
        for (key, value) in metadata.iteritems():
176
            str_metadata[str(key)] = str(value)
177
        is_public = 'true' if public else 'false'
178
        params = {'is_public': is_public, 'disk_format': 'diskdump'}
179
        return self.image.register(name, location, params, str_metadata)
180

    
181
    def share(self, location):
182
        """Share this file with all the users"""
183

    
184
        self.pithos.set_object_sharing(location, "*")
185

    
186
    def object_exists(self, location):
187
        """Check if an object exists in pythos"""
188

    
189
        try:
190
            self.pithos.get_object_info(location)
191
        except ClientError as e:
192
            if e.status == 404:  # Object not found error
193
                return False
194
            else:
195
                raise
196
        return True
197

    
198
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :