def inventory_format(spec):
    from pxul.StringIO import StringIO
    from pxul.os import fullpath

    with StringIO() as sio:

        for group in spec.inventory:
            assert len(group) == 1
            name = group.keys()[0]
            nodes = group.values()[0]

            sio.writeln('[{}]'.format(name))

            for node in nodes:
                ip = node.floating_ip or node.ip

                sio.write('{host} ansible_ssh_host={ip}'\
                          .format(host = node.hostname,
                                  ip   = ip))

                sio.write(' ansible_ssh_private_key={}'\
                          .format(fullpath(node.private_key)))

                sio.write('\n')

            sio.writeln('')

        return sio.getvalue()
def boot(nodes, prefix='', dry_run=False,
         waitForActiveSleep=1,
         waitForActiveTimeout=60,
         **kws):

    nova = get_client()

    for node in nodes:
        node_name = prefix + node.hostname
        print node.hostname, '->', node_name

        image_name = node.image
        flavor_name = node.flavor
        key_name = node.key_name
        net_name = node.network
        sec_groups = node.security_groups

        if dry_run:
            yield node
            continue

        ################################################## upload key if needed

        try:
            print('-> Looking for key {}'.format(key_name))
            nova.keypairs.find(name=key_name)
        except novaclient.exceptions.NotFound:
            print('...not found, adding {} as {}'.format(node.public_key, key_name))
            path = node.public_key
            key  = open(fullpath(path)).read()
            nova.keypairs.create(key_name, key)

        image = nova.images.find(name=image_name)
        flavor = nova.flavors.find(name=flavor_name)
        nics = [{'net-id': nova.networks.find(label=net_name).id}]


        ################################################## boot

        try:
            print('-> Checking if already booted')
            nova.servers.find(name=node_name)
            print '...true'
            continue
        except novaclient.exceptions.NotFound:

            print('-> Creating {}'.format(node_name))
            vm = nova.servers.create(
                node_name,
                image,
                flavor,
                key_name=key_name,
                nics=nics
            )

        def is_active():
            instance = nova.servers.get(vm.id)
            return instance.status == 'ACTIVE'

        print '-> Waiting until ACTIVE ',
        wait_until(is_active, sleep_time=waitForActiveSleep, max_time=waitForActiveTimeout)


        ################################################## security groups

        for name in sec_groups:
            print('-> Adding to security group {}'.format(name))
            vm.add_security_group(name)


        ################################################## floating ip

        if node.create_floating_ip:
            print('-> Adding floating ip')
            try:
                # first try to get a free ip
                floating_ip = nova.floating_ips.findall(instance_id=None)[0]
                print('...using {}'.format(floating_ip))
            except IndexError:
                pool = node.floating_ip_pool
                floating_ip = nova.floating_ips.create(pool=pool)
                print('...allocated {} from pool {}'.format(floating_ip, pool))

            print('...associating')
            vm.add_floating_ip(floating_ip)

            # usefull for regenerating a spec file
            node.floating_ip = floating_ip.ip
            # node.set_dynamic('floating_ip', str(ip.ip))
            print('...done')


        ################################################## internal ip

        print('-> Geting internal ip')
        instance = nova.servers.get(vm.id)
        addresses = instance.addresses[net_name]
        fixed_addresses = [
            a['addr']
            for a in addresses
            if a['OS-EXT-IPS:type'] == 'fixed'
        ]
        assert len(fixed_addresses) == 1, fixed_addresses
        internal_ip = fixed_addresses[0]
        node.ip = internal_ip
        print('...done')

        ################################################## extra discs

        for disk in node.extra_disks:
            # cinder not support yet
            print 'WARNING extra disks not supported yet'
            # node.unset_dynamic('extra_disks')


        ################################################## save
        yield node