I'm not a nova guy.
I don't understand the difference between the OpenStack compute v2 os-interfaces API - and the Openstack compute os-virtual-interfacesv2 extension API.
But rackspace seems to have had to write an extension for the novaclient CLI to make it able to attach a new virtual interfaces for a cloud network to a running instance.
I was thinking I might have to write a new resource to support the api extension (probably just crib from the existing ServerInterface) - but it turned out there's already support in OpenStackSDK to boot instances with extra networks.
Here's what I'm using:
#!/usr/bin/env python import os import requests import sys # default instance and network attributes IMAGE = '09de0a66-3156-48b4-90a5-1cf25a905207' FLAVOR = 'general1-4' REGION = 'DFW' NETWORK = 'my-test' CIDR = '192.168.5.0/24' auth_url = os.environ.get( 'OS_AUTHURL', 'https://identity.api.rackspacecloud.com/v2.0/') # username and *password* must be set in the envrion username = os.environ['OS_USERNAME'] password = os.environ['OS_PASSWORD'] # something about the rackspace cert makes requests mad requests.packages.urllib3.disable_warnings() from openstack import connection from openstack.compute.v2._proxy import _server # the openstacksdk doesn't support admin_pass # https://bugs.launchpad.net/python-openstacksdk/+bug/1528949 class PatchedServerResource(_server.Server): def create_by_id(self, *args, **kwargs): resp = super(PatchedServerResource, self).create_by_id(*args, **kwargs) self._attrs['adminPass'] = resp.get('adminPass') return resp _server.Server = PatchedServerResource conn = connection.Connection(auth_url=auth_url, username=username, password=password) conn.profile.set_region(conn.profile.ALL, REGION) def _get_or_create_network(name): # this doesn't seem to work # network = conn.network.find_network(name) # https://feedback.rackspace.com/forums/298158-network/suggestions/11188446-500-error-response-in-cloud-network-api # so we just search by hand for n in conn.network.networks(): if n.name == name: return n network = conn.network.create_network(name=name) conn.network.create_subnet( network_id=network.id, ip_version='4', cidr=CIDR) return network def main(): try: server_name = sys.argv[1] except IndexError: return 'USAGE: %s <server-name>' % sys.argv[0] image = conn.compute.find_image(IMAGE) flavor = conn.compute.find_flavor(FLAVOR) network = _get_or_create_network(NETWORK) network_ids = ( network.id, # must explicitly include Public & Service networks '11111111-1111-1111-1111-111111111111', '00000000-0000-0000-0000-000000000000', ) server = conn.compute.create_server( name=server_name, image=image, flavor=flavor, networks=[{'uuid': id_} for id_ in network_ids] ) print 'pass', server['adminPass'] server = conn.compute.wait_for_server(server) print 'ip', server.access_ipv4 if __name__ == "__main__": sys.exit(main())
So... that's not terrible. You can see there's a few hacks in there - some defaults are globals for simplicity instead of bothering with argparse - credentials from environ for the same reason - I made the conn instance a global because it's easier to get ahold of in a repl just by importing - there's that weird cert error - some ugly monkey patching to get at admin_pass - another function for finding a network by name to work around something I hit in the Rackspace neutron API.
Having to add the awkward default static uuid's for the Public and Service networks sorta threw me on my first go 'round.
But all in all this works for creating new servers with a private cloud network on the Rackspace cloud. Hooray!