Dwarf is this really cool little project by Juerg Haefliger that provides a subset of the OpenStack APIs to use libvirt on a single host. For some context, here’s the original email that was sent to the OpenStack list. What it does is allow you to use manage a single libvirt host as though it were OpenStack, ie. use nova, glance, and keystone commands to manage libvirt virtual machines.
For some reason I find faking APIs really interesting. I guess a better word than faking would be “compatability” but really what is going on is APIs are being faked. For example, OpenStack has always, as far as I know, provided some Amazon Web Services (AWS) compatibility. OpenStack Swift also can provide Amazon S3 API compatibility. Another example is Cloudscaling providing a GCE compatabile API for OpenStack.
I think fake APIs also suggests that a certain application or service is becoming popular, and so having a little fake OpenStack subset API using Dwarf is a compliment to OpenStack. Also it can help in terms of understanding how OpenStack works. Do you wonder what the keystone catalog does? Well, now you can mess around with it in Dwarf and find out.
As Haefliger says in the README for Dwarf:
First install the Dwarf PPA. I’m running this on Ubuntu Precise 12.04, which is itself a virtual machine, and thus we’ll be doing nested virtualization, which may need to be turned on in some hosts.
ubuntu@dwarf:~$ sudo apt-add-repository ppa:juergh/dwarf
Then install Dwarf.
ubuntu@dwarf:~$ sudo apt-get install dwarf
Now we have a dwarf command.
ubuntu@dwarf:~$ which dwarf
/usr/bin/dwarf
We can start dwarf with service.
root@dwarf:~# service dwarf start
That starts a few processes listening, openstack identity, openstack compute, and openstack images, ie. keystone, nova, and glance respectively.
root@dwarf:/etc# netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:35357 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:8774 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:9292 0.0.0.0:* LISTEN
tcp 0 0 192.168.122.77:22 192.168.122.1:38835 ESTABLISHED
tcp6 0 0 :::22 :::* LISTEN
Need python-novaclient, python-keystoneclient too. I’m going to use pip to get more recent versions of these commands. I guess glance comes from glance-client? Weird.
root@dwarf:~# apt-get install python-pip
root@dwarf:~# pip install python-novaclient
root@dwarf:~# nova --version
2.17.0
root@dwarf:~# glance --version
0.12.0
root@dwarf:~# keystone --version
0.9.0
Create a default openstack rc file and source it. Again note there is no real authentication.
root@dwarf:~# cat dwarfrc
export OS_AUTH_URL=http://127.0.0.1:35357/v2.0/
export OS_COMPUTE_API_VERSION=1.1
export OS_REGION_NAME=dwarf-region
export OS_TENANT_NAME=dwarf-tenant
export OS_USERNAME=dwarf-user
export OS_PASSWORD=dwarf-password
root@dwarf:~# . dwarfrc
And finally we can run some nova commands.
root@dwarf:~# nova list
+----+------+--------+----------+
| ID | Name | Status | Networks |
+----+------+--------+----------+
+----+------+--------+----------+
root@dwarf:~# nova flavor-list
+-----+-----------------+-----------+------+-----------+------+-------+-------------+
| ID | Name | Memory_MB | Disk | Ephemeral | Swap | VCPUs | RXTX_Factor |
+-----+-----------------+-----------+------+-----------+------+-------+-------------+
| 100 | standard.xsmall | 512 | 10 | N/A | | 1 | |
| 101 | standard.small | 768 | 30 | N/A | | 1 | |
| 102 | standard.medium | 1024 | 30 | N/A | | 1 | |
+-----+-----------------+-----------+------+-----------+------+-------+-------------+
root@dwarf:~# keystone catalog
WARNING:keystoneclient.httpclient:Failed to retrieve management_url from token
Service: image
+-------------+----------------------------+
| Property | Value |
+-------------+----------------------------+
| publicURL | http://127.0.0.1:9292/v1.0 |
| region | dwarf-region |
| tenantId | 1000 |
| versionId | 1.0 |
| versionInfo | http://127.0.0.1:9292/v1.0 |
| versionList | http://127.0.0.1:9292 |
+-------------+----------------------------+
Service: compute
+-------------+---------------------------------+
| Property | Value |
+-------------+---------------------------------+
| publicURL | http://127.0.0.1:8774/v1.1/1000 |
| region | dwarf-region |
| tenantId | 1000 |
| versionId | 1.1 |
| versionInfo | http://127.0.0.1:8774/v1.1 |
| versionList | http://127.0.0.1:8774 |
+-------------+---------------------------------+
For some reason the latest python-glanceclient was broken in my install. Not sure if it’s something I did wrong or what, but I ended up using 0.12.0. Note the glance that comes with 12.04 does not have the image-create command.
root@dwarf:/tmp# pip install python-glanceclient==0.12.0
root@dwarf:/tmp# which glance
/usr/local/bin/glance
root@dwarf:/tmp# glance --version
0.12.0
root@dwarf:~# glance index
No handlers could be found for logger "keystoneclient.httpclient"
ID Name Disk Format Container Format Size
------------------------------------ ------------------------------ -------------------- -------------------- --------------
96b8b4cc-bf45-4dc2-add0-c6d0fc96aec4
d0a3d8f7-d336-40d1-b548-fbb5e5e01d8f
ced791fc-bd11-4d91-9eb6-3fe892dd2a6d
40bee026-03a4-4020-88bc-bc0acf9465a6
0a29e5fc-fb0b-487d-b957-f4fd296d71b1
f51efef5-fe73-4957-94c0-bf94038a2685
Interestingly running glance image-create with no options with dwarf creates empty images. I deleted all those and also added a cirros image.
Download the Cirros image. I’m using Cirros because I’m running Dwarf inside a virtual machine, so have limited resources.
root@dwarf:~# wget http://download.cirros-cloud.net/0.3.2/cirros-0.3.2-x86_64-disk.img
Add that image to Dwarf using glance.
root@dwarf:~# glance image-create --name "Cirros" --file cirros-0.3.2-x86_64-disk.img
Now it’s in glance.
root@dwarf:~# glance image-list
No handlers could be found for logger "keystoneclient.httpclient"
+--------------------------------------+--------+-------------+------------------+----------+--------+
| ID | Name | Disk Format | Container Format | Size | Status |
+--------------------------------------+--------+-------------+------------------+----------+--------+
| 56105cdc-00d8-4e69-beae-fbe20abcbe36 | Cirros | | | 13167616 | ACTIVE |
+--------------------------------------+--------+-------------+------------------+----------+--------+
Add a keypair. (Though cirros won’t use it.)
root@dwarf:~# nova keypair-add --pub-key ~/.ssh/id_rsa.pub root
root@dwarf:~# nova keypair-list
+------+-------------------------------------------------+
| Name | Fingerprint |
+------+-------------------------------------------------+
| root | 7f:21:e1:9b:ee:3d:84:89:a5:bc:c1:3e:79:20:e5:c0 |
+------+-------------------------------------------------+
If you are nesting, ie. a vm inside a vm, before going further edit the default libvirt network. Change 192.168.122.0/24 to some other network, such as 10.0.0.0/24. 192.168.122.0/24 will likely already be in use and the default network won’t start, and neither will libvirt based vms.
root@dwarf:~# virsh net-edit default
# Edit to look like this:
default
7fd26ceb-ed87-7887-198e-d9cbc4759b70
</pre>
Start that network.
root@dwarf:~# virsh net-start default
setlocale: No such file or directory
Network default started
root@dwarf:~# brctl show
bridge name bridge id STP enabled interfaces
virbr0 8000.525400a6a92a yes virbr0-nic
Looks good.
Next: boot a vm.
root@dwarf:~# nova boot --flavor 100 --image 56105cdc-00d8-4e69-beae-fbe20abcbe36 --key_name root test1
ERROR: 'NoneType' object has no attribute 'get'
I see there is an error reported, but the vm does indeed get started up. Something to look into, might have to do with the version of nova client being used. I certainly had some trouble with the glance client.
That vm is now running:
root@dwarf:~# virsh list
setlocale: No such file or directory
Id Name State
----------------------------------
2 dwarf-00000003 running
root@dwarf:~# nova list
+--------------------------------------+-------+--------+------------+-------------+-------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+-------+--------+------------+-------------+-------------------+
| 0e8a17bf-4c99-455d-87d6-4eb8d35af1d7 | test1 | ACTIVE | N/A | N/A | private=10.0.0.28 |
+--------------------------------------+-------+--------+------------+-------------+-------------------+
root@dwarf:~# cat /var/lib/libvirt/dnsmasq/default.leases
1404773235 52:54:00:2c:94:14 10.0.0.28 * 01:52:54:00:2c:94:14
root@dwarf:~# ping -c 1 -w 1 10.0.0.28
PING 10.0.0.28 (10.0.0.28) 56(84) bytes of data.
64 bytes from 10.0.0.28: icmp_req=1 ttl=64 time=0.694 ms
--- 10.0.0.28 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.694/0.694/0.694/0.000 ms
ssh into the vm...login "cirros", password "cubswin:)"
root@dwarf:~# ssh cirros@10.0.0.28
The authenticity of host '10.0.0.28 (10.0.0.28)' can't be established.
RSA key fingerprint is 44:8a:7a:ce:25:d6:f6:aa:2f:98:bb:c3:ec:a2:e8:2a.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.0.28' (RSA) to the list of known hosts.
cirros@10.0.0.28's password:
$ ifconfig eth0
eth0 Link encap:Ethernet HWaddr 52:54:00:2C:94:14
inet addr:10.0.0.28 Bcast:10.0.0.255 Mask:255.255.255.0
inet6 addr: fe80::5054:ff:fe2c:9414/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:92 errors:0 dropped:0 overruns:0 frame:0
TX packets:70 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:9758 (9.5 KiB) TX bytes:7502 (7.3 KiB)
$ cat /proc/meminfo | head -1
MemTotal: 503476 kB
In fact Dwarf does at least one nice thing for us in that it'll determine the virtual machines IP address automatically, which libvirt doesn't do.
## Conclusion
Using Dwarf we can boot instances using libvirt and qemu. The [code is out there on github](https://github.com/juergh/dwarf) ready to be hacked on and improved or forked for your own purposes. Once you get your host configured properly and the right nova and glance clients installed it seems to work well.
Thanks [Juerg Haefliger](https://github.com/juergh) for Dwarf. :)