Statistics
| Branch: | Tag: | Revision:

root / docs / developers / showcase.rst @ fe980c71

History | View | Annotate | Download (26.9 kB)

1 6c068db6 Stavros Sachtouris
2 6c068db6 Stavros Sachtouris
Showcase: create a virtual cluster from scratch
3 6c068db6 Stavros Sachtouris
===============================================
4 6c068db6 Stavros Sachtouris
5 6c068db6 Stavros Sachtouris
In this section we will create a virtual cluster, from scratch.
6 6c068db6 Stavros Sachtouris
7 6c068db6 Stavros Sachtouris
Requirements:
8 6c068db6 Stavros Sachtouris
9 6c068db6 Stavros Sachtouris
* A `synnefo <http://www.synnefo.org>`_ deployment with functional *Astakos*,
10 6c068db6 Stavros Sachtouris
    *Pithos+*, *Plankton* and *Cyclades* services.
11 6c068db6 Stavros Sachtouris
12 6c068db6 Stavros Sachtouris
* A kamaki setup, configured with a default cloud (see how to do this with
13 6c068db6 Stavros Sachtouris
    kamaki as a
14 6c068db6 Stavros Sachtouris
    `shell command <../examplesdir/configuration.html#multiple-clouds-in-a-single-configuration>`_ ,
15 6c068db6 Stavros Sachtouris
    or a
16 6c068db6 Stavros Sachtouris
    `python library <config.html#set-a-new-cloud-name-it-new-cloud-and-set-it-as-default>`_.
17 6c068db6 Stavros Sachtouris
18 6c068db6 Stavros Sachtouris
* An image stored at file *./my_image.diskdump* that can run on a predefined
19 6c068db6 Stavros Sachtouris
    hardware flavor, identifiable by the flavor id *42* (see how to create an
20 6c068db6 Stavros Sachtouris
    image with the
21 6c068db6 Stavros Sachtouris
    `synnefo image creator <http://www.synnefo.org/docs/snf-image-creator/latest/index.html>`_
22 6c068db6 Stavros Sachtouris
    ).
23 6c068db6 Stavros Sachtouris
24 6c068db6 Stavros Sachtouris
This is the pseudocode:
25 6c068db6 Stavros Sachtouris
26 6c068db6 Stavros Sachtouris
#. Get credentials and service endpoints, with kamaki config and the
27 6c068db6 Stavros Sachtouris
    **Astakos** *identity* and *account* services
28 6c068db6 Stavros Sachtouris
#. Upload the image file to the **Pithos+** *object-store* service
29 6c068db6 Stavros Sachtouris
#. Register the image file to the **Plankton** *image* service
30 6c068db6 Stavros Sachtouris
#. Create a number of virtual servers to the **Cyclades** *compute* service
31 6c068db6 Stavros Sachtouris
32 6c068db6 Stavros Sachtouris
33 6c068db6 Stavros Sachtouris
Credentials and endpoints
34 6c068db6 Stavros Sachtouris
-------------------------
35 6c068db6 Stavros Sachtouris
36 6c068db6 Stavros Sachtouris
We assume that the kamaki configuration file contains at least one cloud
37 6c068db6 Stavros Sachtouris
configuration, and this configuration is also set as the default cloud for
38 6c068db6 Stavros Sachtouris
kamaki. A cloud configuration is basically a name for the cloud, an
39 6c068db6 Stavros Sachtouris
authentication URL and an authentication TOKEN: the credentials we are looking
40 6c068db6 Stavros Sachtouris
for!
41 6c068db6 Stavros Sachtouris
42 6c068db6 Stavros Sachtouris
This is the plan:
43 6c068db6 Stavros Sachtouris
44 6c068db6 Stavros Sachtouris
#. Get the credentials from the kamaki configuration
45 6c068db6 Stavros Sachtouris
#. Initialize an AstakosClient and test the credentials
46 6c068db6 Stavros Sachtouris
#. Get the endpoints for all services
47 6c068db6 Stavros Sachtouris
48 6c068db6 Stavros Sachtouris
.. code-block:: python
49 6c068db6 Stavros Sachtouris
50 6c068db6 Stavros Sachtouris
    from sys import stderr
51 6c068db6 Stavros Sachtouris
    from kamaki.cli.config import Config, CONFIG_PATH
52 6c068db6 Stavros Sachtouris
    from kamaki.clients.astakos import AstakosClient, ClientError
53 6c068db6 Stavros Sachtouris
54 6c068db6 Stavros Sachtouris
    #  Initialize Config with default values.
55 6c068db6 Stavros Sachtouris
    cnf = Config()
56 6c068db6 Stavros Sachtouris
57 6c068db6 Stavros Sachtouris
    #  1. Get the credentials
58 6c068db6 Stavros Sachtouris
    #  Get default cloud name 
59 6c068db6 Stavros Sachtouris
    try:
60 6c068db6 Stavros Sachtouris
        cloud_name = cnf.get('global', 'default_cloud')
61 6c068db6 Stavros Sachtouris
    except KeyError:
62 6c068db6 Stavros Sachtouris
        stderr.write('No default cloud set in file %s\n' % CONFIG_PATH)
63 6c068db6 Stavros Sachtouris
        raise
64 6c068db6 Stavros Sachtouris
65 6c068db6 Stavros Sachtouris
    #  Get cloud authentication URL and TOKEN
66 6c068db6 Stavros Sachtouris
    try:
67 6c068db6 Stavros Sachtouris
        AUTH_URL = cnf.get_cloud(cloud_name, 'url')
68 6c068db6 Stavros Sachtouris
    except KeyError:
69 6c068db6 Stavros Sachtouris
        stderr.write('No authentication URL in cloud %s\n' % cloud_name)
70 6c068db6 Stavros Sachtouris
        raise
71 6c068db6 Stavros Sachtouris
    try:
72 6c068db6 Stavros Sachtouris
        AUTH_TOKEN = cnf.get_cloud(cloud_name, 'token')
73 6c068db6 Stavros Sachtouris
    except KeyError:
74 6c068db6 Stavros Sachtouris
        stderr.write('No token in cloud %s\n' % cloud_name)
75 6c068db6 Stavros Sachtouris
        raise
76 6c068db6 Stavros Sachtouris
77 6c068db6 Stavros Sachtouris
    #  2. Test the credentials
78 6c068db6 Stavros Sachtouris
    #  Test authentication credentials
79 6c068db6 Stavros Sachtouris
    try:
80 6c068db6 Stavros Sachtouris
        auth = AstakosClient(AUTH_URL, AUTH_TOKEN)
81 6c068db6 Stavros Sachtouris
        auth.authenticate()
82 6c068db6 Stavros Sachtouris
    except ClientError:
83 6c068db6 Stavros Sachtouris
        stderr.write('Athentication failed with url %s and token %s\n' % (
84 6c068db6 Stavros Sachtouris
            AUTH_URL, AUTH_TOKEN))
85 6c068db6 Stavros Sachtouris
        raise
86 6c068db6 Stavros Sachtouris
87 6c068db6 Stavros Sachtouris
    #  3. Get the endpoints
88 6c068db6 Stavros Sachtouris
    #  Identity, Account --> astakos
89 6c068db6 Stavros Sachtouris
    #  Compute --> cyclades
90 6c068db6 Stavros Sachtouris
    #  Object-store --> pithos
91 6c068db6 Stavros Sachtouris
    #  Image --> plankton
92 6c068db6 Stavros Sachtouris
    try:
93 6c068db6 Stavros Sachtouris
        endpoints = dict(
94 6c068db6 Stavros Sachtouris
            astakos=AUTH_URL,
95 6c068db6 Stavros Sachtouris
            cyclades=auth.get_service_endpoints('compute')['publicURL'],
96 6c068db6 Stavros Sachtouris
            pithos=auth.get_service_endpoints('object-store')['publicURL'],
97 6c068db6 Stavros Sachtouris
            plankton=auth.get_service_endpoints('image')['publicURL']
98 6c068db6 Stavros Sachtouris
            )
99 6c068db6 Stavros Sachtouris
        user_id = auth.user_info()['id']
100 6c068db6 Stavros Sachtouris
    except ClientError:
101 6c068db6 Stavros Sachtouris
        stderr.write(
102 6c068db6 Stavros Sachtouris
            'Failed to get user id and endpoints from the identity server\n')
103 6c068db6 Stavros Sachtouris
        raise
104 6c068db6 Stavros Sachtouris
105 6c068db6 Stavros Sachtouris
Upload the image
106 6c068db6 Stavros Sachtouris
----------------
107 6c068db6 Stavros Sachtouris
108 6c068db6 Stavros Sachtouris
We assume there is an image file at the current local directory, at
109 6c068db6 Stavros Sachtouris
*./my_image.diskdump* and we need to upload it to a Pithos+ container. We also
110 6c068db6 Stavros Sachtouris
assume the contains does not currently exist. We will name it *images*.
111 6c068db6 Stavros Sachtouris
112 6c068db6 Stavros Sachtouris
This is the plan:
113 6c068db6 Stavros Sachtouris
114 6c068db6 Stavros Sachtouris
#. Initialize a Pithos+ client
115 6c068db6 Stavros Sachtouris
#. Create the container *images*
116 6c068db6 Stavros Sachtouris
#. Upload the local file to the container
117 6c068db6 Stavros Sachtouris
118 6c068db6 Stavros Sachtouris
.. code-block:: python
119 6c068db6 Stavros Sachtouris
120 6c068db6 Stavros Sachtouris
    from os.path import abspath
121 6c068db6 Stavros Sachtouris
    from kamaki.clients.pithos import PithosClient
122 6c068db6 Stavros Sachtouris
123 6c068db6 Stavros Sachtouris
    CONTAINER = 'images'
124 6c068db6 Stavros Sachtouris
    IMAGE_FILE = 'my_image.diskdump'
125 6c068db6 Stavros Sachtouris
126 6c068db6 Stavros Sachtouris
    #  1. Initialize Pithos+ client and set account to current user
127 6c068db6 Stavros Sachtouris
    try:
128 6c068db6 Stavros Sachtouris
        pithos = PithosClient(endpoints['pithos'], AUTH_TOKEN)
129 6c068db6 Stavros Sachtouris
    except ClientError:
130 6c068db6 Stavros Sachtouris
        stderr.write('Failed to initialize a Pithos+ client\n')
131 6c068db6 Stavros Sachtouris
        raise
132 6c068db6 Stavros Sachtouris
    pithos.account = user_id
133 6c068db6 Stavros Sachtouris
134 6c068db6 Stavros Sachtouris
    #  2. Create the container "images" and let pithos client work with that
135 6c068db6 Stavros Sachtouris
    try:
136 6c068db6 Stavros Sachtouris
        pithos.create_container('images')
137 6c068db6 Stavros Sachtouris
    except ClientError:
138 6c068db6 Stavros Sachtouris
        stderr.write('Failed to create container "image"\n')
139 6c068db6 Stavros Sachtouris
        raise
140 6c068db6 Stavros Sachtouris
    pithos.container = CONTAINER
141 6c068db6 Stavros Sachtouris
142 6c068db6 Stavros Sachtouris
    #  3. Upload
143 6c068db6 Stavros Sachtouris
    with open(abspath(IMAGE_FILE)) as f:
144 6c068db6 Stavros Sachtouris
        try:
145 6c068db6 Stavros Sachtouris
            pithos.upload_object(IMAGE_FILE, f)
146 6c068db6 Stavros Sachtouris
        except ClientError:
147 6c068db6 Stavros Sachtouris
            stderr.write('Failed to upload file %s to container %s\n' % (
148 6c068db6 Stavros Sachtouris
                IMAGE_FILE, CONTAINER))
149 6c068db6 Stavros Sachtouris
            raise
150 6c068db6 Stavros Sachtouris
151 6c068db6 Stavros Sachtouris
Register the image
152 6c068db6 Stavros Sachtouris
------------------
153 6c068db6 Stavros Sachtouris
154 6c068db6 Stavros Sachtouris
Now the image is located at *pithos://<user_id>/images/my_image.diskdump*
155 6c068db6 Stavros Sachtouris
and we want to register it to the Plankton *image* service.
156 6c068db6 Stavros Sachtouris
157 6c068db6 Stavros Sachtouris
.. code-block:: python
158 6c068db6 Stavros Sachtouris
159 6c068db6 Stavros Sachtouris
    from kamaki.clients.image import ImageClient
160 6c068db6 Stavros Sachtouris
161 6c068db6 Stavros Sachtouris
    IMAGE_NAME = 'My image'
162 6c068db6 Stavros Sachtouris
    IMAGE_LOCATION = (user_id, CONTAINER, IMAGE_FILE)
163 6c068db6 Stavros Sachtouris
164 6c068db6 Stavros Sachtouris
    #  3.1 Initialize ImageClient
165 6c068db6 Stavros Sachtouris
    try:
166 6c068db6 Stavros Sachtouris
        plankton = ImageClient(endpoints['plankton'], AUTH_TOKEN)
167 6c068db6 Stavros Sachtouris
    except ClientError:
168 6c068db6 Stavros Sachtouris
        stderr.write('Failed to initialize the Image client client\n')
169 6c068db6 Stavros Sachtouris
        raise
170 6c068db6 Stavros Sachtouris
171 6c068db6 Stavros Sachtouris
    #  3.2 Register the image
172 278fae3f Stavros Sachtouris
    properties = dict(osfamily='linux', root_partition='1')
173 6c068db6 Stavros Sachtouris
    try:
174 6c068db6 Stavros Sachtouris
        image = plankton.image_register(IMAGE_NAME, IMAGE_LOCATION)
175 6c068db6 Stavros Sachtouris
    except ClientError:
176 6c068db6 Stavros Sachtouris
        stderr.write('Failed to register image %s\n' % IMAGE_NAME)
177 6c068db6 Stavros Sachtouris
        raise
178 6c068db6 Stavros Sachtouris
179 6c068db6 Stavros Sachtouris
Create the virtual cluster
180 6c068db6 Stavros Sachtouris
--------------------------
181 6c068db6 Stavros Sachtouris
182 6c068db6 Stavros Sachtouris
In order to build a virtual cluster, we need some information:
183 6c068db6 Stavros Sachtouris
184 6c068db6 Stavros Sachtouris
* an image id. We can get them from *image['id']* (the id of the image we
185 6c068db6 Stavros Sachtouris
    have just created)
186 6c068db6 Stavros Sachtouris
* a hardware flavor. Assume we have picked the flavor with id *42*
187 6c068db6 Stavros Sachtouris
* a set of names for our virtual servers. We will name them *cluster1*,
188 6c068db6 Stavros Sachtouris
    *cluster2*, etc.
189 6c068db6 Stavros Sachtouris
190 6c068db6 Stavros Sachtouris
Here is the plan:
191 6c068db6 Stavros Sachtouris
192 6c068db6 Stavros Sachtouris
#. Initialize a Cyclades/Compute client
193 6c068db6 Stavros Sachtouris
#. Create a number of virtual servers. Their name should be prefixed as
194 6c068db6 Stavros Sachtouris
    "cluster"
195 6c068db6 Stavros Sachtouris
196 6c068db6 Stavros Sachtouris
.. code-block:: python
197 6c068db6 Stavros Sachtouris
198 6c068db6 Stavros Sachtouris
    #  4.  Create  virtual  cluster
199 6c068db6 Stavros Sachtouris
    from kamaki.clients.cyclades import CycladesClient
200 6c068db6 Stavros Sachtouris
201 6c068db6 Stavros Sachtouris
    FLAVOR_ID = 42
202 6c068db6 Stavros Sachtouris
    IMAGE_ID = image['id']
203 6c068db6 Stavros Sachtouris
    CLUSTER_SIZE = 2
204 6c068db6 Stavros Sachtouris
    CLUSTER_PREFIX = 'cluster'
205 6c068db6 Stavros Sachtouris
206 6c068db6 Stavros Sachtouris
    #  4.1 Initialize a cyclades client
207 6c068db6 Stavros Sachtouris
    try:
208 6c068db6 Stavros Sachtouris
        cyclades = CycladesClient(endpoints['cyclades'], AUTH_TOKEN)
209 6c068db6 Stavros Sachtouris
    except ClientError:
210 6c068db6 Stavros Sachtouris
        stderr.write('Failed to initialize cyclades client\n')
211 6c068db6 Stavros Sachtouris
        raise
212 6c068db6 Stavros Sachtouris
213 6c068db6 Stavros Sachtouris
    #  4.2 Create 2 servers prefixed as "cluster"
214 6c068db6 Stavros Sachtouris
    servers = []
215 6c068db6 Stavros Sachtouris
    for i in range(1, CLUSTER_SIZE + 1):
216 6c068db6 Stavros Sachtouris
        server_name = '%s%s' % (CLUSTER_PREFIX, i)
217 6c068db6 Stavros Sachtouris
        try:
218 6c068db6 Stavros Sachtouris
            servers.append(
219 6c068db6 Stavros Sachtouris
                cyclades.create_server(server_name, FLAVOR_ID, IMAGE_ID))
220 6c068db6 Stavros Sachtouris
        except ClientError:
221 6c068db6 Stavros Sachtouris
            stderr.write('Failed while creating server %s\n' % server_name)
222 6c068db6 Stavros Sachtouris
            raise
223 6c068db6 Stavros Sachtouris
224 6c068db6 Stavros Sachtouris
Some improvements
225 6c068db6 Stavros Sachtouris
-----------------
226 6c068db6 Stavros Sachtouris
227 6c068db6 Stavros Sachtouris
Progress Bars
228 6c068db6 Stavros Sachtouris
'''''''''''''
229 6c068db6 Stavros Sachtouris
230 6c068db6 Stavros Sachtouris
Uploading an image might take a while. You can wait patiently, or you can use a
231 6c068db6 Stavros Sachtouris
progress generator. Even better, combine a generator with the progress bar
232 6c068db6 Stavros Sachtouris
package that comes with kamaki. The upload_object method accepts two generators
233 6c068db6 Stavros Sachtouris
as parameters: one for calculating local file hashes and another for uploading
234 6c068db6 Stavros Sachtouris
235 6c068db6 Stavros Sachtouris
.. code-block:: python
236 6c068db6 Stavros Sachtouris
237 6c068db6 Stavros Sachtouris
    from progress.bar import Bar
238 6c068db6 Stavros Sachtouris
239 6c068db6 Stavros Sachtouris
    def hash_gen(n):
240 6c068db6 Stavros Sachtouris
        bar = Bar('Calculating hashes...')
241 6c068db6 Stavros Sachtouris
        for i in bar.iter(range(int(n))):
242 6c068db6 Stavros Sachtouris
            yield
243 6c068db6 Stavros Sachtouris
        yield
244 6c068db6 Stavros Sachtouris
245 6c068db6 Stavros Sachtouris
    def upload_gen(n):
246 6c068db6 Stavros Sachtouris
        bar = Bar('Uploading...')
247 6c068db6 Stavros Sachtouris
        for i in bar.iter(range(int(n))):
248 6c068db6 Stavros Sachtouris
            yield
249 6c068db6 Stavros Sachtouris
        yield
250 6c068db6 Stavros Sachtouris
251 6c068db6 Stavros Sachtouris
    ...
252 6c068db6 Stavros Sachtouris
    pithos.upload_object(
253 6c068db6 Stavros Sachtouris
        IMAGE_FILE, f, hash_cb=hash_gen, upload_cb=upload_gen)
254 6c068db6 Stavros Sachtouris
255 6c068db6 Stavros Sachtouris
We can create a method to produce progress bar generators, and use it in other
256 6c068db6 Stavros Sachtouris
methods as well:
257 6c068db6 Stavros Sachtouris
258 6c068db6 Stavros Sachtouris
.. code-block:: python
259 6c068db6 Stavros Sachtouris
260 6c068db6 Stavros Sachtouris
    try:
261 6c068db6 Stavros Sachtouris
        from progress.bar import Bar
262 6c068db6 Stavros Sachtouris
263 6c068db6 Stavros Sachtouris
        def create_pb(msg):
264 6c068db6 Stavros Sachtouris
            def generator(n):
265 6c068db6 Stavros Sachtouris
                bar=Bar(msg)
266 6c068db6 Stavros Sachtouris
                for i in bar.iter(range(int(n))):
267 6c068db6 Stavros Sachtouris
                    yield
268 6c068db6 Stavros Sachtouris
                yield
269 6c068db6 Stavros Sachtouris
            return generator
270 6c068db6 Stavros Sachtouris
    except ImportError:
271 6c068db6 Stavros Sachtouris
        stderr.write('Suggestion: install python-progress\n')
272 6c068db6 Stavros Sachtouris
        def create_pb(msg):
273 6c068db6 Stavros Sachtouris
            return None
274 6c068db6 Stavros Sachtouris
275 6c068db6 Stavros Sachtouris
    ...
276 6c068db6 Stavros Sachtouris
    pithos.upload_object(
277 6c068db6 Stavros Sachtouris
        IMAGE_FILE, f,
278 6c068db6 Stavros Sachtouris
        hash_cb=create_pb('Calculating hashes...'),
279 6c068db6 Stavros Sachtouris
        upload_cb=create_pb('Uploading...'))
280 6c068db6 Stavros Sachtouris
281 6c068db6 Stavros Sachtouris
Wait for servers to built
282 6c068db6 Stavros Sachtouris
'''''''''''''''''''''''''
283 6c068db6 Stavros Sachtouris
284 6c068db6 Stavros Sachtouris
When a create_server method is finished successfully, a server is being built.
285 6c068db6 Stavros Sachtouris
Usually, it takes a while for a server to built. Fortunately, there is a wait
286 6c068db6 Stavros Sachtouris
method in the kamaki cyclades client. It can use a progress bar too!
287 6c068db6 Stavros Sachtouris
288 6c068db6 Stavros Sachtouris
.. code-block:: python
289 6c068db6 Stavros Sachtouris
290 6c068db6 Stavros Sachtouris
    #  4.2 Create 2 servers prefixed as "cluster"
291 6c068db6 Stavros Sachtouris
    ...
292 6c068db6 Stavros Sachtouris
293 6c068db6 Stavros Sachtouris
    # 4.3 Wait for servers to built
294 6c068db6 Stavros Sachtouris
    for server in servers:
295 6c068db6 Stavros Sachtouris
        cyclades.wait_server(server['id'])
296 6c068db6 Stavros Sachtouris
297 6c068db6 Stavros Sachtouris
Asynchronous server creation
298 6c068db6 Stavros Sachtouris
''''''''''''''''''''''''''''
299 6c068db6 Stavros Sachtouris
300 6c068db6 Stavros Sachtouris
In case of a large virtual cluster, it might be faster to spawn the servers
301 6c068db6 Stavros Sachtouris
with asynchronous requests. Kamaki clients offer an automated mechanism for
302 6c068db6 Stavros Sachtouris
asynchronous requests.
303 6c068db6 Stavros Sachtouris
304 6c068db6 Stavros Sachtouris
.. code-block:: python
305 6c068db6 Stavros Sachtouris
306 6c068db6 Stavros Sachtouris
    #  4.2 Create 2 servers prefixed as "cluster"
307 6c068db6 Stavros Sachtouris
    create_params = [dict(
308 6c068db6 Stavros Sachtouris
        name='%s%s' % (CLUSTER_PREFIX, i),
309 6c068db6 Stavros Sachtouris
        flavor_id=FLAVOR_ID,
310 6c068db6 Stavros Sachtouris
        image_id=IMAGE_ID) for i in range(1, CLUSTER_SIZE + 1)]
311 6c068db6 Stavros Sachtouris
    try:
312 6c068db6 Stavros Sachtouris
        servers = cyclades.async_run(cyclades.create_server, create_params)
313 6c068db6 Stavros Sachtouris
    except ClientError:
314 6c068db6 Stavros Sachtouris
        stderr.write('Failed while creating servers\n')
315 6c068db6 Stavros Sachtouris
        raise
316 6c068db6 Stavros Sachtouris
317 6c068db6 Stavros Sachtouris
Clean up virtual cluster
318 6c068db6 Stavros Sachtouris
''''''''''''''''''''''''
319 6c068db6 Stavros Sachtouris
320 6c068db6 Stavros Sachtouris
We need to clean up Cyclades from servers left from previous cluster creations.
321 6c068db6 Stavros Sachtouris
This clean up will destroy all servers prefixed with "cluster". It will run
322 6c068db6 Stavros Sachtouris
before the cluster creation:
323 6c068db6 Stavros Sachtouris
324 6c068db6 Stavros Sachtouris
.. code-block:: python
325 6c068db6 Stavros Sachtouris
326 6c068db6 Stavros Sachtouris
    #  4.2 Clean up virtual cluster
327 6c068db6 Stavros Sachtouris
    to_delete = [server for server in cyclades.list_servers(detail=True) if (
328 6c068db6 Stavros Sachtouris
        server['name'].startswith(CLUSTER_PREFIX))]
329 6c068db6 Stavros Sachtouris
    for server in to_delete:
330 6c068db6 Stavros Sachtouris
        cyclades.delete_server(server['id'])
331 6c068db6 Stavros Sachtouris
    for server in to_delete:
332 6c068db6 Stavros Sachtouris
        cyclades.wait_server(
333 6c068db6 Stavros Sachtouris
            server['id'], server['status'],
334 6c068db6 Stavros Sachtouris
            wait_cb=create_pb('Deleting %s...' % server['name']))
335 6c068db6 Stavros Sachtouris
336 6c068db6 Stavros Sachtouris
    #  4.3 Create 2 servers prefixed as "cluster"
337 6c068db6 Stavros Sachtouris
    ...
338 6c068db6 Stavros Sachtouris
339 6c068db6 Stavros Sachtouris
Inject ssh keys
340 6c068db6 Stavros Sachtouris
'''''''''''''''
341 6c068db6 Stavros Sachtouris
342 6c068db6 Stavros Sachtouris
When a server is created, the returned value contains a filed "adminPass". This
343 6c068db6 Stavros Sachtouris
field can be used to manually log into the server.
344 6c068db6 Stavros Sachtouris
345 6c068db6 Stavros Sachtouris
An easier way is to
346 6c068db6 Stavros Sachtouris
`inject the ssh keys <../examplesdir/server.html#inject-ssh-keys-to-a-debian-server>`_
347 6c068db6 Stavros Sachtouris
of the users who are going to use the virtual servers.
348 6c068db6 Stavros Sachtouris
349 6c068db6 Stavros Sachtouris
Assuming that we have collected the keys in a file named *rsa.pub*, we can
350 6c068db6 Stavros Sachtouris
inject them into each server, with the personality argument
351 6c068db6 Stavros Sachtouris
352 6c068db6 Stavros Sachtouris
.. code-block:: python
353 6c068db6 Stavros Sachtouris
354 6c068db6 Stavros Sachtouris
    SSH_KEYS = 'rsa.pub'
355 6c068db6 Stavros Sachtouris
356 6c068db6 Stavros Sachtouris
    ...
357 6c068db6 Stavros Sachtouris
358 6c068db6 Stavros Sachtouris
    #  4.3 Create 2 servers prefixed as "cluster"
359 7de6773d Stavros Sachtouris
    personality = []
360 6c068db6 Stavros Sachtouris
    if SSH_KEYS:
361 7de6773d Stavros Sachtouris
        with open(abspath(SSH_KEYS)) as f:
362 7de6773d Stavros Sachtouris
            personality.append(dict(
363 6c068db6 Stavros Sachtouris
                contents=b64encode(f.read()),
364 6c068db6 Stavros Sachtouris
                path='/root/.ssh/authorized_keys',
365 5cd1aee1 Stavros Sachtouris
                owner='root', group='root', mode=0600)
366 7de6773d Stavros Sachtouris
            personality.append(dict(
367 7de6773d Stavros Sachtouris
                contents=b64encode('StrictHostKeyChecking no'),
368 7de6773d Stavros Sachtouris
                path='/root/.ssh/config',
369 5cd1aee1 Stavros Sachtouris
                owner='root', group='root', mode=0600))
370 7de6773d Stavros Sachtouris
371 6c068db6 Stavros Sachtouris
    create_params = [dict(
372 6c068db6 Stavros Sachtouris
        name='%s%s' % (CLUSTER_PREFIX, i),
373 6c068db6 Stavros Sachtouris
        flavor_id=FLAVOR_ID,
374 6c068db6 Stavros Sachtouris
        image_id=IMAGE_ID,
375 6c068db6 Stavros Sachtouris
        personality=personality) for i in range(1, CLUSTER_SIZE + 1)]
376 6c068db6 Stavros Sachtouris
    ...
377 6c068db6 Stavros Sachtouris
378 7de6773d Stavros Sachtouris
Save server passwords in a file
379 7de6773d Stavros Sachtouris
'''''''''''''''''''''''''''''''
380 7de6773d Stavros Sachtouris
381 7de6773d Stavros Sachtouris
A last touch: define a local file to store the created server information,
382 7de6773d Stavros Sachtouris
including the superuser password.
383 7de6773d Stavros Sachtouris
384 7de6773d Stavros Sachtouris
.. code-block:: python
385 7de6773d Stavros Sachtouris
        
386 7de6773d Stavros Sachtouris
    #  4.4 Store passwords in file 
387 7de6773d Stavros Sachtouris
    SERVER_INFO = 'servers.txt'
388 7de6773d Stavros Sachtouris
    with open(abspath(SERVER_INFO), 'w+') as f:
389 7de6773d Stavros Sachtouris
        from json import dump
390 7de6773d Stavros Sachtouris
        dump(servers, f, intend=2)
391 7de6773d Stavros Sachtouris
392 7de6773d Stavros Sachtouris
    #  4.5 Wait for 2 servers to built
393 7de6773d Stavros Sachtouris
    ...
394 7de6773d Stavros Sachtouris
395 7de6773d Stavros Sachtouris
Errors and logs
396 7de6773d Stavros Sachtouris
'''''''''''''''
397 7de6773d Stavros Sachtouris
398 7de6773d Stavros Sachtouris
Developers may use the kamaki tools for
399 7de6773d Stavros Sachtouris
`error handling <clients-api.html#error-handling>`_ and
400 7de6773d Stavros Sachtouris
`logging <logging.html>`_, or implement their own methods.
401 7de6773d Stavros Sachtouris
402 7de6773d Stavros Sachtouris
To demonstrate, we will modify the container creation code to warn users if the
403 7de6773d Stavros Sachtouris
container already exists. We need a stream logger for the warning and a
404 7de6773d Stavros Sachtouris
knowledge of the expected return values for the *create_container* method.
405 7de6773d Stavros Sachtouris
406 7de6773d Stavros Sachtouris
First, let's get the logger.
407 7de6773d Stavros Sachtouris
408 7de6773d Stavros Sachtouris
.. code-block:: python
409 7de6773d Stavros Sachtouris
410 7de6773d Stavros Sachtouris
    from kamaki.cli.logger import add_stream_logger, get_logger
411 7de6773d Stavros Sachtouris
412 7de6773d Stavros Sachtouris
    add_stream_logger(__name__)
413 7de6773d Stavros Sachtouris
    log = get_logger(__name__)
414 7de6773d Stavros Sachtouris
415 7de6773d Stavros Sachtouris
The *create_container* method makes an HTTP request to the pithos server. It
416 7de6773d Stavros Sachtouris
considers the request succesfull if the status code of the response is 201
417 7de6773d Stavros Sachtouris
(created) or 202 (accepted). These status codes mean that the container has
418 7de6773d Stavros Sachtouris
been created or that it was already there anyway, respectively.
419 7de6773d Stavros Sachtouris
420 7de6773d Stavros Sachtouris
We will force *create_container* to raise an error in case of a 202 response.
421 7de6773d Stavros Sachtouris
This can be done by instructing *create_container* to accept only 201 as a
422 7de6773d Stavros Sachtouris
successful status.
423 7de6773d Stavros Sachtouris
424 7de6773d Stavros Sachtouris
.. code-block:: python
425 7de6773d Stavros Sachtouris
426 7de6773d Stavros Sachtouris
    try:
427 7de6773d Stavros Sachtouris
        pithos.create_container(CONTAINER, success=(201, ))
428 7de6773d Stavros Sachtouris
    except ClientError as ce:
429 7de6773d Stavros Sachtouris
        if ce.status in (202, ):
430 7de6773d Stavros Sachtouris
            log.warning('Container %s already exists' % CONTAINER')
431 7de6773d Stavros Sachtouris
        else:
432 7de6773d Stavros Sachtouris
            log.debug('Failed to create container %s' % CONTAINER)
433 7de6773d Stavros Sachtouris
            raise
434 7de6773d Stavros Sachtouris
    log.info('Container %s is ready' % CONTAINER)
435 7de6773d Stavros Sachtouris
436 7de6773d Stavros Sachtouris
create a cluster from scratch
437 7de6773d Stavros Sachtouris
-----------------------------
438 7de6773d Stavros Sachtouris
439 7de6773d Stavros Sachtouris
We are ready to create a module that uses kamaki to create a cluster from
440 7de6773d Stavros Sachtouris
scratch. We revised the code by grouping functionality in methods and using
441 7de6773d Stavros Sachtouris
logging more. We also added some command line interaction candy.
442 7de6773d Stavros Sachtouris
443 7de6773d Stavros Sachtouris
.. code-block:: python
444 7de6773d Stavros Sachtouris
445 7de6773d Stavros Sachtouris
    #!/usr/bin/env python
446 7de6773d Stavros Sachtouris
447 7de6773d Stavros Sachtouris
    from sys import argv
448 7de6773d Stavros Sachtouris
    from os.path import abspath
449 7de6773d Stavros Sachtouris
    from base64 import b64encode
450 7de6773d Stavros Sachtouris
    from kamaki.clients import ClientError
451 7de6773d Stavros Sachtouris
    from kamaki.cli.logger import get_logger, add_file_logger
452 7de6773d Stavros Sachtouris
    from logging import DEBUG
453 7de6773d Stavros Sachtouris
454 7de6773d Stavros Sachtouris
    #  Define loggers
455 7de6773d Stavros Sachtouris
    log = get_logger(__name__)
456 7de6773d Stavros Sachtouris
    add_file_logger('kamaki.clients', DEBUG, '%s.log' % __name__)
457 7de6773d Stavros Sachtouris
    add_file_logger(__name__, DEBUG, '%s.log' % __name__)
458 7de6773d Stavros Sachtouris
459 7de6773d Stavros Sachtouris
    #  Create progress bar generator
460 7de6773d Stavros Sachtouris
    try:
461 7de6773d Stavros Sachtouris
        from progress.bar import Bar
462 7de6773d Stavros Sachtouris
463 7de6773d Stavros Sachtouris
        def create_pb(msg):
464 7de6773d Stavros Sachtouris
            def generator(n):
465 7de6773d Stavros Sachtouris
                bar=Bar(msg)
466 7de6773d Stavros Sachtouris
                for i in bar.iter(range(int(n))):
467 7de6773d Stavros Sachtouris
                    yield
468 7de6773d Stavros Sachtouris
                yield
469 7de6773d Stavros Sachtouris
            return generator
470 7de6773d Stavros Sachtouris
    except ImportError:
471 7de6773d Stavros Sachtouris
        log.warning('Suggestion: install python-progress')
472 7de6773d Stavros Sachtouris
        def create_pb(msg):
473 7de6773d Stavros Sachtouris
            return None
474 7de6773d Stavros Sachtouris
475 7de6773d Stavros Sachtouris
476 7de6773d Stavros Sachtouris
    #  kamaki.config
477 7de6773d Stavros Sachtouris
    #  Identity,Account / Astakos
478 7de6773d Stavros Sachtouris
479 7de6773d Stavros Sachtouris
    def init_astakos():
480 7de6773d Stavros Sachtouris
        from kamaki.clients.astakos import AstakosClient
481 7de6773d Stavros Sachtouris
        from kamaki.cli.config import Config, CONFIG_PATH
482 7de6773d Stavros Sachtouris
483 7de6773d Stavros Sachtouris
        print(' Get the credentials')
484 7de6773d Stavros Sachtouris
        cnf = Config()
485 7de6773d Stavros Sachtouris
486 7de6773d Stavros Sachtouris
        #  Get default cloud name
487 7de6773d Stavros Sachtouris
        try:
488 7de6773d Stavros Sachtouris
            cloud_name = cnf.get('global', 'default_cloud')
489 7de6773d Stavros Sachtouris
        except KeyError:
490 7de6773d Stavros Sachtouris
            log.debug('No default cloud set in file %' % CONFIG_PATH)
491 7de6773d Stavros Sachtouris
            raise
492 7de6773d Stavros Sachtouris
493 7de6773d Stavros Sachtouris
        try:
494 7de6773d Stavros Sachtouris
            AUTH_URL = cnf.get_cloud(cloud_name, 'url')
495 7de6773d Stavros Sachtouris
        except KeyError:
496 7de6773d Stavros Sachtouris
            log.debug('No authentication URL in cloud %s' % cloud_name)
497 7de6773d Stavros Sachtouris
            raise
498 7de6773d Stavros Sachtouris
        try:
499 7de6773d Stavros Sachtouris
            AUTH_TOKEN = cnf.get_cloud(cloud_name, 'token')
500 7de6773d Stavros Sachtouris
        except KeyError:
501 7de6773d Stavros Sachtouris
            log.debug('No token in cloud %s' % cloud_name)
502 7de6773d Stavros Sachtouris
            raise
503 7de6773d Stavros Sachtouris
504 7de6773d Stavros Sachtouris
        print(' Test the credentials')
505 7de6773d Stavros Sachtouris
        try:
506 7de6773d Stavros Sachtouris
            auth = AstakosClient(AUTH_URL, AUTH_TOKEN)
507 7de6773d Stavros Sachtouris
            auth.authenticate()
508 7de6773d Stavros Sachtouris
        except ClientError:
509 7de6773d Stavros Sachtouris
            log.debug('Athentication failed with url %s and token %s' % (
510 7de6773d Stavros Sachtouris
                AUTH_URL, AUTH_TOKEN))
511 7de6773d Stavros Sachtouris
            raise
512 7de6773d Stavros Sachtouris
513 7de6773d Stavros Sachtouris
        return auth, AUTH_TOKEN
514 7de6773d Stavros Sachtouris
515 7de6773d Stavros Sachtouris
516 7de6773d Stavros Sachtouris
    def endpoints_and_user_id(auth):
517 7de6773d Stavros Sachtouris
        print(' Get the endpoints')
518 7de6773d Stavros Sachtouris
        try:
519 7de6773d Stavros Sachtouris
            endpoints = dict(
520 7de6773d Stavros Sachtouris
                astakos=auth.get_service_endpoints('identity')['publicURL'],
521 7de6773d Stavros Sachtouris
                cyclades=auth.get_service_endpoints('compute')['publicURL'],
522 7de6773d Stavros Sachtouris
                pithos=auth.get_service_endpoints('object-store')['publicURL'],
523 7de6773d Stavros Sachtouris
                plankton=auth.get_service_endpoints('image')['publicURL']
524 7de6773d Stavros Sachtouris
                )
525 7de6773d Stavros Sachtouris
            user_id = auth.user_info()['id']
526 7de6773d Stavros Sachtouris
        except ClientError:
527 7de6773d Stavros Sachtouris
            print('Failed to get endpoints & user_id from identity server')
528 7de6773d Stavros Sachtouris
            raise
529 7de6773d Stavros Sachtouris
        return endpoints, user_id
530 7de6773d Stavros Sachtouris
531 7de6773d Stavros Sachtouris
532 7de6773d Stavros Sachtouris
    #  Object-store / Pithos+
533 7de6773d Stavros Sachtouris
534 7de6773d Stavros Sachtouris
    def init_pithos(endpoint, token, user_id):
535 7de6773d Stavros Sachtouris
        from kamaki.clients.pithos import PithosClient
536 7de6773d Stavros Sachtouris
537 7de6773d Stavros Sachtouris
        print(' Initialize Pithos+ client and set account to user uuid')
538 7de6773d Stavros Sachtouris
        try:
539 7de6773d Stavros Sachtouris
            return PithosClient(endpoint, token, user_id)
540 7de6773d Stavros Sachtouris
        except ClientError:
541 7de6773d Stavros Sachtouris
            log.debug('Failed to initialize a Pithos+ client')
542 7de6773d Stavros Sachtouris
            raise
543 7de6773d Stavros Sachtouris
544 7de6773d Stavros Sachtouris
545 7de6773d Stavros Sachtouris
    def upload_image(pithos, container, image_path):
546 7de6773d Stavros Sachtouris
547 7de6773d Stavros Sachtouris
        print(' Create the container "images" and use it')
548 7de6773d Stavros Sachtouris
        try:
549 7de6773d Stavros Sachtouris
            pithos.create_container(container, success=(201, ))
550 7de6773d Stavros Sachtouris
        except ClientError as ce:
551 7de6773d Stavros Sachtouris
            if ce.status in (202, ):
552 7de6773d Stavros Sachtouris
                log.warning('Container %s already exists' % container)
553 7de6773d Stavros Sachtouris
            else:
554 7de6773d Stavros Sachtouris
                log.debug('Failed to create container %s' % container)
555 7de6773d Stavros Sachtouris
                raise
556 7de6773d Stavros Sachtouris
        pithos.container = container
557 7de6773d Stavros Sachtouris
558 7de6773d Stavros Sachtouris
        print(' Upload to "images"')
559 7de6773d Stavros Sachtouris
        with open(abspath(image_path)) as f:
560 7de6773d Stavros Sachtouris
            try:
561 7de6773d Stavros Sachtouris
                pithos.upload_object(
562 7de6773d Stavros Sachtouris
                    image_path, f,
563 7de6773d Stavros Sachtouris
                    hash_cb=create_pb('  Calculating hashes...'),
564 7de6773d Stavros Sachtouris
                    upload_cb=create_pb('  Uploading...'))
565 7de6773d Stavros Sachtouris
            except ClientError:
566 7de6773d Stavros Sachtouris
                log.debug('Failed to upload file %s to container %s' % (
567 7de6773d Stavros Sachtouris
                    image_path, container))
568 7de6773d Stavros Sachtouris
                raise
569 7de6773d Stavros Sachtouris
570 7de6773d Stavros Sachtouris
571 7de6773d Stavros Sachtouris
    #  Image / Plankton
572 7de6773d Stavros Sachtouris
573 7de6773d Stavros Sachtouris
    def init_plankton(endpoint, token):
574 7de6773d Stavros Sachtouris
        from kamaki.clients.image import ImageClient
575 7de6773d Stavros Sachtouris
576 7de6773d Stavros Sachtouris
        print(' Initialize ImageClient')
577 7de6773d Stavros Sachtouris
        try:
578 7de6773d Stavros Sachtouris
            return ImageClient(endpoint, token)
579 7de6773d Stavros Sachtouris
        except ClientError:
580 7de6773d Stavros Sachtouris
            log.debug('Failed to initialize the Image client')
581 7de6773d Stavros Sachtouris
            raise
582 7de6773d Stavros Sachtouris
583 7de6773d Stavros Sachtouris
584 278fae3f Stavros Sachtouris
    def register_image(plankton, name, user_id, container, path, properties):
585 7de6773d Stavros Sachtouris
586 7de6773d Stavros Sachtouris
        image_location = (user_id, container, path)
587 7de6773d Stavros Sachtouris
        print(' Register the image')
588 7de6773d Stavros Sachtouris
        try:
589 278fae3f Stavros Sachtouris
             return plankton.register(name, image_location, properties)
590 7de6773d Stavros Sachtouris
        except ClientError:
591 7de6773d Stavros Sachtouris
            log.debug('Failed to register image %s' % name)
592 7de6773d Stavros Sachtouris
            raise
593 7de6773d Stavros Sachtouris
594 7de6773d Stavros Sachtouris
595 7de6773d Stavros Sachtouris
    #  Compute / Cyclades
596 7de6773d Stavros Sachtouris
597 7de6773d Stavros Sachtouris
    def init_cyclades(endpoint, token):
598 7de6773d Stavros Sachtouris
        from kamaki.clients.cyclades import CycladesClient
599 7de6773d Stavros Sachtouris
600 7de6773d Stavros Sachtouris
        print(' Initialize a cyclades client')
601 7de6773d Stavros Sachtouris
        try:
602 7de6773d Stavros Sachtouris
            return CycladesClient(endpoint, token)
603 7de6773d Stavros Sachtouris
        except ClientError:
604 7de6773d Stavros Sachtouris
            log.debug('Failed to initialize cyclades client')
605 7de6773d Stavros Sachtouris
            raise
606 7de6773d Stavros Sachtouris
607 7de6773d Stavros Sachtouris
608 7de6773d Stavros Sachtouris
    class Cluster(object):
609 7de6773d Stavros Sachtouris
610 7de6773d Stavros Sachtouris
        def __init__(self, cyclades, prefix, flavor_id, image_id, size):
611 7de6773d Stavros Sachtouris
            self.client = cyclades
612 7de6773d Stavros Sachtouris
            self.prefix, self.size = prefix, int(size)
613 7de6773d Stavros Sachtouris
            self.flavor_id, self.image_id = flavor_id, image_id
614 7de6773d Stavros Sachtouris
615 7de6773d Stavros Sachtouris
        def list(self):
616 7de6773d Stavros Sachtouris
            return [s for s in self.client.list_servers(detail=True) if (
617 7de6773d Stavros Sachtouris
                s['name'].startswith(self.prefix))]
618 7de6773d Stavros Sachtouris
619 7de6773d Stavros Sachtouris
        def clean_up(self):
620 7de6773d Stavros Sachtouris
            to_delete = self.list()
621 7de6773d Stavros Sachtouris
            print('  There are %s servers to clean up' % len(to_delete))
622 7de6773d Stavros Sachtouris
            for server in to_delete:
623 7de6773d Stavros Sachtouris
                self.client.delete_server(server['id'])
624 7de6773d Stavros Sachtouris
            for server in to_delete:
625 7de6773d Stavros Sachtouris
                self.client.wait_server(
626 7de6773d Stavros Sachtouris
                    server['id'], server['status'],
627 7de6773d Stavros Sachtouris
                    wait_cb=create_pb(' Deleting %s...' % server['name']))
628 7de6773d Stavros Sachtouris
629 7de6773d Stavros Sachtouris
        def _personality(self, ssh_keys_path='', pub_keys_path=''):
630 7de6773d Stavros Sachtouris
            personality = []
631 7de6773d Stavros Sachtouris
            if ssh_keys_path:
632 7de6773d Stavros Sachtouris
                with open(abspath(ssh_keys_path)) as f:
633 7de6773d Stavros Sachtouris
                    personality.append(dict(
634 7de6773d Stavros Sachtouris
                        contents=b64encode(f.read()),
635 7de6773d Stavros Sachtouris
                        path='/root/.ssh/id_rsa',
636 5cd1aee1 Stavros Sachtouris
                        owner='root', group='root', mode=0600))
637 7de6773d Stavros Sachtouris
            if pub_keys_path:
638 7de6773d Stavros Sachtouris
                with open(abspath(pub_keys_path)) as f:
639 7de6773d Stavros Sachtouris
                    personality.append(dict(
640 7de6773d Stavros Sachtouris
                        contents=b64encode(f.read()),
641 7de6773d Stavros Sachtouris
                        path='/root/.ssh/authorized_keys',
642 5cd1aee1 Stavros Sachtouris
                        owner='root', group='root', mode=0600))
643 7de6773d Stavros Sachtouris
            if ssh_keys_path or pub_keys_path:
644 7de6773d Stavros Sachtouris
                    personality.append(dict(
645 7de6773d Stavros Sachtouris
                        contents=b64encode('StrictHostKeyChecking no'),
646 7de6773d Stavros Sachtouris
                        path='/root/.ssh/config',
647 5cd1aee1 Stavros Sachtouris
                        owner='root', group='root', mode=0600))
648 7de6773d Stavros Sachtouris
            return personality
649 7de6773d Stavros Sachtouris
650 7de6773d Stavros Sachtouris
        def create(self, ssh_k_path='', pub_k_path='', server_log_path=''):
651 7de6773d Stavros Sachtouris
            print('\n Create %s servers prefixed as %s' % (
652 7de6773d Stavros Sachtouris
                self.size, self.prefix))
653 7de6773d Stavros Sachtouris
            servers = []
654 7de6773d Stavros Sachtouris
            for i in range(1, self.size + 1):
655 7de6773d Stavros Sachtouris
                try:
656 7de6773d Stavros Sachtouris
                    server_name = '%s%s' % (self.prefix, i)
657 7de6773d Stavros Sachtouris
                    servers.append(self.client.create_server(
658 7de6773d Stavros Sachtouris
                        server_name, self.flavor_id, self.image_id,
659 7de6773d Stavros Sachtouris
                        personality=self._personality(ssh_k_path, pub_k_path)))
660 7de6773d Stavros Sachtouris
                except ClientError:
661 7de6773d Stavros Sachtouris
                    log.debug('Failed while creating server %s' % server_name)
662 7de6773d Stavros Sachtouris
                    raise
663 7de6773d Stavros Sachtouris
664 7de6773d Stavros Sachtouris
            if server_log_path:
665 7de6773d Stavros Sachtouris
                print(' Store passwords in file %s' % server_log_path)
666 7de6773d Stavros Sachtouris
                with open(abspath(server_log_path), 'w+') as f:
667 7de6773d Stavros Sachtouris
                    from json import dump
668 7de6773d Stavros Sachtouris
                    dump(servers, f, indent=2)
669 7de6773d Stavros Sachtouris
670 7de6773d Stavros Sachtouris
            print(' Wait for %s servers to built' % self.size)
671 7de6773d Stavros Sachtouris
            for server in servers:
672 7de6773d Stavros Sachtouris
                new_status = self.client.wait_server(
673 7de6773d Stavros Sachtouris
                    server['id'],
674 7de6773d Stavros Sachtouris
                    wait_cb=create_pb(' Creating %s...' % server['name']))
675 7de6773d Stavros Sachtouris
                print(' Status for server %s is %s' % (
676 7de6773d Stavros Sachtouris
                    server['name'], new_status or 'not changed yet'))
677 7de6773d Stavros Sachtouris
            return servers
678 7de6773d Stavros Sachtouris
679 7de6773d Stavros Sachtouris
680 7de6773d Stavros Sachtouris
    def main(opts):
681 7de6773d Stavros Sachtouris
682 7de6773d Stavros Sachtouris
        print('1.  Credentials  and  Endpoints')
683 7de6773d Stavros Sachtouris
        auth, token = init_astakos()
684 7de6773d Stavros Sachtouris
        endpoints, user_id = endpoints_and_user_id(auth)
685 7de6773d Stavros Sachtouris
686 7de6773d Stavros Sachtouris
        print('2.  Upload  the  image  file')
687 7de6773d Stavros Sachtouris
        pithos = init_pithos(endpoints['pithos'], token, user_id)
688 7de6773d Stavros Sachtouris
689 7de6773d Stavros Sachtouris
        upload_image(pithos, opts.container, opts.imagefile)
690 7de6773d Stavros Sachtouris
691 7de6773d Stavros Sachtouris
        print('3.  Register  the  image')
692 7de6773d Stavros Sachtouris
        plankton = init_plankton(endpoints['plankton'], token)
693 7de6773d Stavros Sachtouris
694 7de6773d Stavros Sachtouris
        image = register_image(
695 278fae3f Stavros Sachtouris
            plankton, 'my image', user_id, opts.container, opts.imagefile,
696 278fae3f Stavros Sachtouris
            properties=dict(
697 278fae3f Stavros Sachtouris
                osfamily=opts.osfamily, root_partition=opts.rootpartition))
698 7de6773d Stavros Sachtouris
699 7de6773d Stavros Sachtouris
        print('4.  Create  virtual  cluster')
700 7de6773d Stavros Sachtouris
        cluster = Cluster(
701 7de6773d Stavros Sachtouris
            cyclades = init_cyclades(endpoints['cyclades'], token),
702 7de6773d Stavros Sachtouris
            prefix=opts.prefix,
703 7de6773d Stavros Sachtouris
            flavor_id=opts.flavorid,
704 7de6773d Stavros Sachtouris
            image_id=image['id'],
705 7de6773d Stavros Sachtouris
            size=opts.clustersize)
706 7de6773d Stavros Sachtouris
        if opts.delete_stale:
707 7de6773d Stavros Sachtouris
            cluster.clean_up()
708 7de6773d Stavros Sachtouris
        servers = cluster.create(
709 7de6773d Stavros Sachtouris
            opts.sshkeypath, opts.pubkeypath, opts.serverlogpath)
710 7de6773d Stavros Sachtouris
711 7de6773d Stavros Sachtouris
        #  Group servers
712 7de6773d Stavros Sachtouris
        cluster_servers = cluster.list()
713 7de6773d Stavros Sachtouris
714 7de6773d Stavros Sachtouris
        active = [s for s in cluster_servers if s['status'] == 'ACTIVE']
715 7de6773d Stavros Sachtouris
        print('%s cluster servers are ACTIVE' % len(active))
716 7de6773d Stavros Sachtouris
717 7de6773d Stavros Sachtouris
        attached = [s for s in cluster_servers if s['attachments']]
718 7de6773d Stavros Sachtouris
        print('%s cluster servers are attached to networks' % len(attached))
719 7de6773d Stavros Sachtouris
720 7de6773d Stavros Sachtouris
        build = [s for s in cluster_servers if s['status'] == 'BUILD']
721 7de6773d Stavros Sachtouris
        print('%s cluster servers are being built' % len(build))
722 7de6773d Stavros Sachtouris
723 7de6773d Stavros Sachtouris
        error = [s for s in cluster_servers if s['status'] in ('ERROR')]
724 7de6773d Stavros Sachtouris
        print('%s cluster servers failed (ERROR satus)' % len(error))
725 7de6773d Stavros Sachtouris
726 7de6773d Stavros Sachtouris
727 7de6773d Stavros Sachtouris
    if __name__ == '__main__':
728 7de6773d Stavros Sachtouris
729 7de6773d Stavros Sachtouris
        #  Add some interaction candy
730 7de6773d Stavros Sachtouris
        from optparse import OptionParser
731 7de6773d Stavros Sachtouris
732 7de6773d Stavros Sachtouris
        kw = {}
733 7de6773d Stavros Sachtouris
        kw['usage'] = '%prog [options]'
734 7de6773d Stavros Sachtouris
        kw['description'] = '%prog deploys a compute cluster on Synnefo w. kamaki'
735 7de6773d Stavros Sachtouris
736 7de6773d Stavros Sachtouris
        parser = OptionParser(**kw)
737 7de6773d Stavros Sachtouris
        parser.disable_interspersed_args()
738 7de6773d Stavros Sachtouris
        parser.add_option('--prefix',
739 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='prefix',
740 7de6773d Stavros Sachtouris
                          help='The prefix to use for naming cluster nodes',
741 7de6773d Stavros Sachtouris
                          default='cluster')
742 7de6773d Stavros Sachtouris
        parser.add_option('--clustersize',
743 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='clustersize',
744 7de6773d Stavros Sachtouris
                          help='Number of virtual cluster nodes to create ',
745 7de6773d Stavros Sachtouris
                          default=2)
746 7de6773d Stavros Sachtouris
        parser.add_option('--flavor-id',
747 7de6773d Stavros Sachtouris
                          action='store', type='int', dest='flavorid',
748 7de6773d Stavros Sachtouris
                          metavar='FLAVOR ID',
749 7de6773d Stavros Sachtouris
                          help='Choose flavor id for the virtual hardware '
750 7de6773d Stavros Sachtouris
                               'of cluster nodes',
751 7de6773d Stavros Sachtouris
                          default=42)
752 7de6773d Stavros Sachtouris
        parser.add_option('--image-file',
753 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='imagefile',
754 7de6773d Stavros Sachtouris
                          metavar='IMAGE FILE PATH',
755 7de6773d Stavros Sachtouris
                          help='The image file to upload and register ',
756 7de6773d Stavros Sachtouris
                          default='my_image.diskdump')
757 7de6773d Stavros Sachtouris
        parser.add_option('--delete-stale',
758 7de6773d Stavros Sachtouris
                          action='store_true', dest='delete_stale',
759 7de6773d Stavros Sachtouris
                          help='Delete stale servers from previous runs, whose '
760 7de6773d Stavros Sachtouris
                               'name starts with the specified prefix, see '
761 7de6773d Stavros Sachtouris
                               '--prefix',
762 7de6773d Stavros Sachtouris
                          default=False)
763 7de6773d Stavros Sachtouris
        parser.add_option('--container',
764 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='container',
765 7de6773d Stavros Sachtouris
                          metavar='PITHOS+ CONTAINER',
766 7de6773d Stavros Sachtouris
                          help='The Pithos+ container to store image file',
767 7de6773d Stavros Sachtouris
                          default='images')
768 7de6773d Stavros Sachtouris
        parser.add_option('--ssh-key-path',
769 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='sshkeypath',
770 7de6773d Stavros Sachtouris
                          metavar='PATH OF SSH KEYS',
771 7de6773d Stavros Sachtouris
                          help='The ssh keys to inject to server (e.g., id_rsa) ',
772 7de6773d Stavros Sachtouris
                          default='')
773 7de6773d Stavros Sachtouris
        parser.add_option('--pub-key-path',
774 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='pubkeypath',
775 7de6773d Stavros Sachtouris
                          metavar='PATH OF PUBLIC KEYS',
776 7de6773d Stavros Sachtouris
                          help='The public keys to inject to server',
777 7de6773d Stavros Sachtouris
                          default='')
778 7de6773d Stavros Sachtouris
        parser.add_option('--server-log-path',
779 7de6773d Stavros Sachtouris
                          action='store', type='string', dest='serverlogpath',
780 7de6773d Stavros Sachtouris
                          metavar='FILE TO LOG THE VIRTUAL SERVERS',
781 7de6773d Stavros Sachtouris
                          help='Where to store information on created servers '
782 7de6773d Stavros Sachtouris
                               'including superuser passwords',
783 7de6773d Stavros Sachtouris
                          default='')
784 278fae3f Stavros Sachtouris
        parser.add_option('--image-osfamily',
785 278fae3f Stavros Sachtouris
                          action='store', type='string', dest='osfamily',
786 278fae3f Stavros Sachtouris
                          metavar='OS FAMILY',
787 278fae3f Stavros Sachtouris
                          help='linux, windows, etc.',
788 278fae3f Stavros Sachtouris
                          default='linux')
789 278fae3f Stavros Sachtouris
        parser.add_option('--image-root-partition',
790 278fae3f Stavros Sachtouris
                          action='store', type='string', dest='rootpartition',
791 278fae3f Stavros Sachtouris
                          metavar='IMAGE ROOT PARTITION',
792 278fae3f Stavros Sachtouris
                          help='The partition where the root home is ',
793 278fae3f Stavros Sachtouris
                          default='1')
794 7de6773d Stavros Sachtouris
795 7de6773d Stavros Sachtouris
        opts, args = parser.parse_args(argv[1:])
796 7de6773d Stavros Sachtouris
797 7de6773d Stavros Sachtouris
        main(opts)