multiple changes:
authorPaul Tonelli <paul.tonelli@logilab.fr>
Tue, 05 Mar 2013 15:17:53 +0100
changeset 6 7fc32398ed73
parent 5 430383a302c7
child 7 123e86b7f5f7
multiple changes: - create openstack_client salt module (non-functionnal) - new svg file added to complement readme and provide better understanding
_modules/openstack_client.py
_modules/vm_create.py
others/fonctionnement.svg
--- a/_modules/openstack_client.py	Thu Feb 28 16:47:40 2013 +0100
+++ b/_modules/openstack_client.py	Tue Mar 05 15:17:53 2013 +0100
@@ -9,55 +9,76 @@
 from os import environ
 from glance import client as glance_client
 
-def _set_credentials(id_elements=None, force_update=False):
+# Import third party libs
+HAS_NOVA = False
+try:
+    from novaclient.v1_1 import client
+    HAS_NOVA = True
+except ImportError:
+    pass
+
+_FLAVOR='m1.lvm'
+
+def _auth_nova():
+    '''
+    Set up nova credentials
+    identical to _auth in /usr/share/pyshared/salt/modules/nova.py
+    '''
+    user = __salt__['config.option']('keystone.user')
+    password = __salt__['config.option']('keystone.password')
+    tenant = __salt__['config.option']('keystone.tenant')
+    auth_url = __salt__['config.option']('keystone.auth_url')
+    return client.Client(
+        user, password, tenant, auth_url, service_type="compute"
+    )
+
+def _auth_glance(id_elements=None, force_update=False):
     '''
     really ugly, but works
-    until credentials expire
+    to use glance with cmd.run
     '''
     if id_elements == None:
         id_elements={
-                'OS_AUTH_URL': 'http://control.example.com:5000/v2.0',
-                'OS_TENANT_ID': '00000000000000000000000000000000',
-                'OS_TENANT_NAME':'tenant',
-                'OS_USERNAME': 'username',
-                'OS_PASSWORD': 'password'
+                'OS_AUTH_URL': __salt__['config.option']('keystone.auth_url'),
+                'OS_TENANT_ID': __salt__['config.option']('keystone.tenant_id'),
+                'OS_TENANT_NAME': __salt__['config.option']('keystone.tenant'),
+                'OS_USERNAME': __salt__['config.option']('keystone.user'),
+                'OS_PASSWORD': __salt__['config.option']('keystone.password')
                 }
         for key in id_elements:
             if force_update or key not in environ:
                 environ[key] = id_elements[key]
-    return True
+    return glance_client.get_client(host='0.0.0.0')
 
 def delete_image(id_image):
-     '''
+    '''
     delete image from glance 
-
+     
     CLI Example::
       
         salt '*'  openstack_client.delete_image 00000000-0000-0000-0000-000000000000
     '''
     print __salt__['cmd.run']("glance delete " + id_image)
 
-
 def get_running_instances():
-    _set_credentials()
+    _auth_glance()
     data = __salt__['nova.list']()
     for i in data:
         print str(i) + " len : " + str(len(i))
-    gc = glance_client.get_client(host='0.0.0.0')
     print gc.get_images()
     return True
 
-def test_pillars():
-    pillars_val = __salt__['pillar.raw']()['master']
-    print pillars_val
-    for i in pillars_val:
-        if i.find("keystone") != -1:
-            print i + " : " + pillars_val[i]
-    return True
+#def test_pillars():
+#    pillars_val = __salt__['pillar.raw']()['master']
+#    print pillars_val
+#    for i in pillars_val:
+#        if i.find("keystone") != -1:
+#            print i + " : " + pillars_val[i]
+#    return True
 
 def upload_to_glance(path, filename, disk_format, other_params, 
         image_name=None):
-    _set_credentials()
+    g_client = _auth_glance()
     if image_name == None:
         image_name = filename.replace(".img","")
     #glance upload initrd, kernel and get ids
@@ -69,4 +90,64 @@
     print "Uploaded " + disk_format + " with id : " + id_image
     return id_image
 
+#def unpause(image_id):
+#    '''
+#    unpause first instance found with image matching id (not instance id)
+#    '''
+#    nt_ks = _auth_nova()
+#    for item in nt_ks.servers.list():
+#        if item.image['id'] == image_id:
+#            if item.status == 'PAUSED':
+#                print item.unpause()
+#                return True
+#    return False
+#
+#def resume(image_id):
+#    '''
+#    resume first instance found with image matching id (not instance id)
+#    '''
+#    nt_ks = _auth_nova()
+#    for item in nt_ks.servers.list():
+#        if item.image['id'] == image_id:
+#            if item.status == 'SUSPENDED':
+#                print item.resume()
+#                return True
+#    return False
 
+def item_list():
+    '''
+    Template for writing list functions
+    Return a list of available items (nova items-list)
+
+    CLI Example::
+
+        salt '*' nova.item_list
+    '''
+    nt_ks = _auth_nova()
+    ret = []
+    for item in nt_ks.servers.list():
+        ret.append(item.__dict__)
+        #ret[item.name] = {
+        #        'name': item.name,
+        #    }
+    return ret
+
+def force_running_container(image_id,flavor=_FLAVOR): #should be used in state
+    '''if a container is active, use it, else create it '''
+    nt_ks = _auth_nova()
+    matching_instances = [machine for machine in nt_ks.servers.list() 
+            if item.nk.servers.list()[0].image['id'] == image_id]
+    if len(matching_instances) == 1:
+        machine = matching_instances[0]
+        if machine.status == 'PAUSED':
+            machine.unpause()
+        if machine.status == 'SUSPENDED':
+            machine.resume()
+        while machine.status != 'ACTIVE':
+            sleep(0.1) # wait for the machine to activate
+    elif len(matching_instances) == 0: #create image
+        nt_ks
+    else:
+        assert False
+    pass
+
--- a/_modules/vm_create.py	Thu Feb 28 16:47:40 2013 +0100
+++ b/_modules/vm_create.py	Tue Mar 05 15:17:53 2013 +0100
@@ -3,8 +3,8 @@
 translation of a bash script to create a flavored virtual machine
 '''
 
-PROXY_ADDRESS = "http://proxy.logilab.priv:3142"
-DEFAULT_PATH = "/mnt"
+PROXY_ADDRESS = 'http://proxy.logilab.priv:3142'
+DEFAULT_PATH = '/mnt'
 
 from os import listdir, remove, stat, environ
 def _get_images(path):
@@ -12,27 +12,27 @@
     find .img files in path
     '''
     images = [element for element in listdir(path) 
-            if element.split(".")[-1] == "img"]
+            if element.split('.')[-1] == 'img']
     return images
 
-def _create_apt_proxy(path="/etc/apt/apt.conf.d/01proxy",
+def _create_apt_proxy(path='/etc/apt/apt.conf.d/01proxy',
         proxy=PROXY_ADDRESS):
     proxy_file = open(path,'w')
     proxy_file.write(proxy)
     proxy_file.close()
     return path
 
-def _create_policy_script(path="/usr/sbin/policy-rc.d",
-        content="exit 101"):
+def _create_policy_script(path='/usr/sbin/policy-rc.d',
+        content='exit 101'):
     policy_file = open(path,'w')
-    policy_file.write("#!/bin/sh")
+    policy_file.write('#!/bin/sh')
     policy_file.write(content)
     __salt__['file.check_perms'](path, {}, 'root', 'root', '755')
     policy_file.close()
     return path
 
 def _chroot_exec(chroot_path, command):
-    return __salt__['cmd.run']("chroot " + chroot_path + " " + command)
+    return __salt__['cmd.run']('chroot ' + chroot_path + ' ' + command)
 
 def umount_images(path):
     '''
@@ -45,7 +45,7 @@
     mounts = sorted([mount for mount in __salt__['mount.active']() 
         if mount.find(path) != -1], reverse = True)
     for mount in mounts:
-        print "umounting " + mount
+        print 'umounting ' + mount
         print __salt__['mount.umount'](mount)
 
 def remove_images(path):
@@ -57,8 +57,8 @@
         salt '*' vm_create.remove_images /mnt
     '''
     for image in _get_images(path):
-        print "removing " + path + "/" + image
-        remove(path + "/"+ image)
+        print 'removing ' + path + '/' + image
+        remove(path + '/'+ image)
 
 def revert(path=DEFAULT_PATH, files_to_delete=()):
     '''
@@ -71,7 +71,7 @@
     umount_images(path)
     remove_images(path)
     for file_to_delete in files_to_delete:
-        remove(path + "/" +file_to_delete)
+        remove(path + '/' +file_to_delete)
        
 def resize_and_check(path, image_name, new_size=0):
     '''
@@ -83,16 +83,16 @@
         salt '*' vm_create.resize_and_check /mnt raring_image.img 4
         salt '*' vm_create.resize_and_check /mnt raring_image.img
     '''
-    current_size = stat(path + "/" + image_name).st_size/pow(1024, 2)
+    current_size = stat(path + '/' + image_name).st_size/pow(1024, 2)
     if new_size > current_size:
-        print __salt__['cmd.run']("dd if=/dev/zero bs=1024k count=" 
-                + str(new_size - current_size) + " >> " + path + "/" 
+        print __salt__['cmd.run']('dd if=/dev/zero bs=1024k count=' 
+                + str(new_size - current_size) + ' >> ' + path + '/' 
                 + image_name)
-        minimize = ""
+        minimize = ''
     else:
-        minimize = "-M "
-    print __salt__['cmd.run']("e2fsck -n -f " + path + "/" + image_name)
-    print __salt__['cmd.run']("resize2fs -f " + minimize + path + "/" 
+        minimize = '-M '
+    print __salt__['cmd.run']('e2fsck -n -f ' + path + '/' + image_name)
+    print __salt__['cmd.run']('resize2fs -f ' + minimize + path + '/' 
             + image_name)
 
 def mount_image(path, archive_name, new_size = 4):
@@ -103,24 +103,24 @@
       
         salt '*' vm_create.resize_and_check /mnt raring_image.tar.gz
     '''
-    print __salt__['file.makedirs'](path + "/") 
-    assert archive_name.split('.')[-1] == "gz"
+    print __salt__['file.makedirs'](path + '/') 
+    assert archive_name.split('.')[-1] == 'gz'
     remove_images(path)
     assert len(_get_images(path)) == 0
-    print __salt__['cmd.run']("tar xzf " + path + "/" + archive_name + " --wildcards -C " 
-            + path + """ "*.img" """)
+    print __salt__['cmd.run']('tar xzf ' + path + '/' + archive_name + ' --wildcards -C ' 
+            + path + ' "*.img" ')
     images = _get_images(path)
     assert len(images) == 1
     image_name = images[0]
     resize_and_check(path, image_name, new_size * 1024)
-    image_dirname = image_name.replace(".img","")
-    print __salt__['file.makedirs'](path + "/" + image_dirname + "/") 
-    print __salt__['mount.mount'](path + "/" + image_dirname, path + "/" 
-            + image_name, opts="loop")
-    print __salt__['mount.mount'](path + "/" + image_dirname + "/proc", "none",
-            fstype="proc")
-    print __salt__['mount.mount'](path + "/" + image_dirname + "/dev/pts", 
-            "none", fstype="devpts")
+    image_dirname = image_name.replace('.img','')
+    print __salt__['file.makedirs'](path + '/' + image_dirname + '/') 
+    print __salt__['mount.mount'](path + '/' + image_dirname, path + '/'
+            + image_name, opts='loop')
+    print __salt__['mount.mount'](path + '/' + image_dirname + '/proc', 'none',
+            fstype='proc')
+    print __salt__['mount.mount'](path + '/' + image_dirname + '/dev/pts', 
+            'none', fstype='devpts')
     return image_dirname, image_name
 
 def update_image(path, image_dirname, create_proxy=True, create_policy=True):
@@ -139,14 +139,14 @@
     '''
     files_to_delete = []
     if create_proxy:
-        files_to_delete.append(_create_apt_proxy(path + "/" + image_dirname 
-            + "/etc/apt/apt.conf.d/01proxy"))
+        files_to_delete.append(_create_apt_proxy(path + '/' + image_dirname 
+            + '/etc/apt/apt.conf.d/01proxy'))
     if create_policy:
-    files_to_delete.append(_create_policy_script(path + "/" + image_dirname 
-        + "/usr/bin/policy-rc.d"))
-    _chroot_exec(image_dirname, "apt-get remove whoopsie")
-    _chroot_exec(image_dirname, "apt-get update && apt-get upgrade")
-    _chroot_exec(image_dirname, "apt-get install salt-minion")
+        files_to_delete.append(_create_policy_script(path + '/' + image_dirname 
+            + '/usr/bin/policy-rc.d'))
+    _chroot_exec(image_dirname, 'apt-get remove whoopsie')
+    _chroot_exec(image_dirname, 'apt-get update && apt-get upgrade')
+    _chroot_exec(image_dirname, 'apt-get install salt-minion')
     return files_to_delete
 
 def flavor_image(path, image_dirname, flavor_filename):
@@ -165,14 +165,14 @@
         salt '*' vm_create.get_initrd_kernel /mnt raring_image
     '''
     return_files = []
-    for filetype in ("vmlinuz","initrd"):
-        myfiles = [myfile for myfile in listdir(path + "/" + image_dirname 
-            + "/boot/") 
+    for filetype in ('vmlinuz','initrd'):
+        myfiles = [myfile for myfile in listdir(path + '/' + image_dirname 
+            + '/boot/') 
                 if myfile.find(filetype) != -1]
         assert len(myfiles) == 1
         myfile = myfiles[0]
-        __salt__['cmd.run']("cp " + path + "/" + image_dirname + "/boot/" 
-                + myfile + " " + path)
+        __salt__['cmd.run']('cp ' + path + '/' + image_dirname + '/boot/'
+                + myfile + ' ' + path)
         return_files.append(myfile)
     return tuple(return_files)
 
@@ -191,16 +191,15 @@
     umount_images(image_dirname)
     resize_and_check(path, image_name)
     if upload_to_glance:
-        print "will now upload to glance server"
-        id_kernel = __salt__["openstack_client.upload_to_glance"](path, kernel_name, "aki", "", image_dirname)
-        id_ramdisk = __salt__["openstack_client.upload_to_glance"](path, ramdisk_name, "ari", "", image_dirname)
-        id_image = __salt__["openstack_client.upload_to_glance"](path, image_name, "ami", "kernel_id="
-                + id_kernel + " ramdisk_id=" + id_ramdisk)
+        print 'will now upload to glance server'
+        id_kernel = __salt__['openstack_client.upload_to_glance'](path, kernel_name, 'aki', '', image_dirname)
+        id_ramdisk = __salt__['openstack_client.upload_to_glance'](path, ramdisk_name, 'ari', '', image_dirname)
+        id_image = __salt__['openstack_client.upload_to_glance'](path, image_name, 'ami', 'kernel_id='
+                + id_kernel + ' ramdisk_id=' + id_ramdisk)
     else:
-        print "Done, no upload to do"
+        print 'Done, no upload to do'
     #clean
     revert(path, (kernel_name, ramdisk_name))
     return True
 
-def instanciate_container(container_id):
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/others/fonctionnement.svg	Tue Mar 05 15:17:53 2013 +0100
@@ -0,0 +1,535 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="744.09448819"
+   height="1052.3622047"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="New document 1">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Mend"
+       style="overflow:visible;">
+      <path
+         id="path3727"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+         transform="scale(0.4) rotate(180) translate(10,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="TriangleOutM"
+       style="overflow:visible">
+      <path
+         id="path3864"
+         d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+         transform="scale(0.4)" />
+    </marker>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective3604"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3604-5"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3663"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3693"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4752"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4786"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4786-1"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4817"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4817-2"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective5246"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Mend-1"
+       style="overflow:visible">
+      <path
+         id="path3727-4"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+    </marker>
+    <inkscape:perspective
+       id="perspective5284"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Mend-6"
+       style="overflow:visible">
+      <path
+         id="path3727-2"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+    </marker>
+    <inkscape:perspective
+       id="perspective5320"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <marker
+       inkscape:stockid="Arrow1Mend"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="Arrow1Mend-2"
+       style="overflow:visible">
+      <path
+         id="path3727-47"
+         d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+    </marker>
+    <inkscape:perspective
+       id="perspective5354"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0"
+       refX="0"
+       id="TriangleOutM-9"
+       style="overflow:visible">
+      <path
+         id="path3864-8"
+         d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+         transform="scale(0.4,0.4)" />
+    </marker>
+    <inkscape:perspective
+       id="perspective5382"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="410.86848"
+     inkscape:cy="855.36997"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:snap-global="true"
+     inkscape:window-width="1916"
+     inkscape:window-height="1161"
+     inkscape:window-x="0"
+     inkscape:window-y="18"
+     inkscape:window-maximized="0">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2816" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="180"
+       y="72.362183"
+       id="text3592"><tspan
+         sodipodi:role="line"
+         id="tspan3594"
+         x="180"
+         y="72.362183">Niveau 1</tspan></text>
+    <g
+       sodipodi:type="inkscape:box3d"
+       style="fill:#ff00ff;fill-opacity:0"
+       id="g3637"
+       inkscape:perspectiveID="#perspective10"
+       inkscape:corner0="0.1058729 : 0.73097356 : 0 : 1"
+       inkscape:corner7="-0.35853924 : 0.45367151 : 0.25 : 1">
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3649"
+         style="fill:#e9e9ff;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="11"
+         d="m 411.59526,116.42302 214.42316,-213.466061 0,311.064781 -214.42316,106.92049 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3639"
+         style="fill:#353564;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="6"
+         d="m 336.42857,23.790755 0,250.753995 75.16669,46.39748 0,-204.51921 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3641"
+         style="fill:#4d4d9f;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="5"
+         d="M 336.42857,23.790755 580,-339.93558 626.01842,-97.043041 411.59526,116.42302 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3647"
+         style="fill:#afafde;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="13"
+         d="M 336.42857,274.54475 580,92.362179 626.01842,214.02174 411.59526,320.94223 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3645"
+         style="fill:#d7d7ff;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="14"
+         d="m 580,-339.93558 0,432.297759 46.01842,121.659561 0,-311.064781 z" />
+      <path
+         sodipodi:type="inkscape:box3dside"
+         id="path3643"
+         style="fill:#8686bf;fill-rule:evenodd;stroke:none"
+         inkscape:box3dsidetype="3"
+         d="M 336.42857,23.790755 580,-339.93558 580,92.362179 336.42857,274.54475 z" />
+    </g>
+    <rect
+       style="fill:#ff00ff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2818-0"
+       width="300"
+       height="10"
+       x="350"
+       y="72.362183" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="440"
+       y="52.362183"
+       id="text3679"><tspan
+         sodipodi:role="line"
+         id="tspan3681"
+         x="440"
+         y="52.362183">OPENSTACK</tspan></text>
+    <rect
+       style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect3683"
+       width="120"
+       height="10"
+       x="360"
+       y="172.36218" />
+    <rect
+       style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect3683-7"
+       width="100"
+       height="50"
+       x="370"
+       y="122.36218" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="418.41797"
+       y="142.36218"
+       id="text3707"><tspan
+         sodipodi:role="line"
+         id="tspan3709"
+         x="418.41797"
+         y="142.36218">AMI</tspan><tspan
+         sodipodi:role="line"
+         x="418.41797"
+         y="167.36218"
+         id="tspan3711">CREATOR</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#TriangleOutM)"
+       d="m 420,192.36218 0,40 70,0"
+       id="path3713" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="500"
+       y="242.36218"
+       id="text4163"><tspan
+         sodipodi:role="line"
+         id="tspan4165"
+         x="500"
+         y="242.36218">chroot</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Mend);stroke-miterlimit:4;stroke-dasharray:none"
+       d="m 420,92.362183 0,19.999997"
+       id="path4541" />
+    <rect
+       style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect3683-7-2"
+       width="280"
+       height="50"
+       x="360"
+       y="22.362183" />
+    <rect
+       style="fill:#ff00ff;fill-opacity:0;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect4766"
+       width="70"
+       height="70"
+       x="30"
+       y="42.362183" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="65.117188"
+       y="72.362183"
+       id="text4768"><tspan
+         sodipodi:role="line"
+         id="tspan4770"
+         x="65.117188"
+         y="72.362183">Repo</tspan><tspan
+         sodipodi:role="line"
+         x="65.117188"
+         y="97.362183"
+         id="tspan4772">HG</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 170,62.36218 0,20 110,0 0,-35 -95,0 -15,15 z"
+       id="path4776"
+       sodipodi:nodetypes="cccccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="550"
+       y="152.36218"
+       id="text3592-5"><tspan
+         sodipodi:role="line"
+         id="tspan3594-6"
+         x="550"
+         y="152.36218">Niveau 2</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 540,142.36218 0,20 110,0 0,-35 -95,0 -15,15 z"
+       id="path4776-1"
+       sodipodi:nodetypes="cccccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="590"
+       y="242.36218"
+       id="text3592-9"><tspan
+         sodipodi:role="line"
+         id="tspan3594-3"
+         x="590"
+         y="242.36218">Niveau 3</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="m 580,232.36218 0,20 110,0 0,-35 -95,0 -15,15 z"
+       id="path4776-4"
+       sodipodi:nodetypes="cccccc" />
+    <flowRoot
+       xml:space="preserve"
+       id="flowRoot4850"
+       style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Bitstream Vera Sans;font-style:normal;font-weight:normal;font-size:20"><flowRegion
+         id="flowRegion4852"><rect
+           id="rect4854"
+           width="200"
+           height="182.85715"
+           x="120.71429"
+           y="154.50504" /></flowRegion><flowPara
+         id="flowPara4856"></flowPara></flowRoot>    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+       d="m -30,72.362183 60,0"
+       id="path4858" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="-80"
+       y="72.362183"
+       id="text5046"><tspan
+         sodipodi:role="line"
+         id="tspan5048"
+         x="-80"
+         y="72.362183">push</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+       d="m 100,72.362183 30,0 40,0"
+       id="path5050"
+       sodipodi:nodetypes="ccc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+       d="m 280,62.362183 30,0 50,0"
+       id="path5050-9"
+       sodipodi:nodetypes="ccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="110.71429"
+       y="61.647896"
+       id="text5266"><tspan
+         sodipodi:role="line"
+         id="tspan5268"
+         x="110.71429"
+         y="61.647896">lance</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="290"
+       y="12.362183"
+       id="text5270"><tspan
+         sodipodi:role="line"
+         x="290"
+         y="12.362183"
+         id="tspan5308">vérifie / force</tspan><tspan
+         sodipodi:role="line"
+         x="290"
+         y="37.362183"
+         id="tspan5274">dispo</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+       d="m 280,72.362183 30,0 0,79.999997 60,0"
+       id="path5050-9-1"
+       sodipodi:nodetypes="cccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="260"
+       y="172.36218"
+       id="text5304"><tspan
+         sodipodi:role="line"
+         id="tspan5306"
+         x="260"
+         y="172.36218">ssh/salt</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow1Mend)"
+       d="m 470,152.36218 40,0 30,0"
+       id="path5050-9-6"
+       sodipodi:nodetypes="ccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="490"
+       y="172.36218"
+       id="text5340"><tspan
+         sodipodi:role="line"
+         id="tspan5342"
+         x="490"
+         y="172.36218">salt</tspan><tspan
+         sodipodi:role="line"
+         x="490"
+         y="197.36218"
+         id="tspan5344">highstate</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#TriangleOutM)"
+       d="m 700,232.36218 25,0 0,-184.999997 -80,0"
+       id="path3713-1"
+       sodipodi:nodetypes="cccc" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+       x="730"
+       y="122.36218"
+       id="text5340-9"><tspan
+         sodipodi:role="line"
+         x="730"
+         y="122.36218"
+         id="tspan5344-0">upload </tspan><tspan
+         sodipodi:role="line"
+         x="730"
+         y="147.36218"
+         id="tspan5404">instances</tspan></text>
+  </g>
+</svg>