def create(self): """Create a vm and vmi, find or create a network, and attach the vmi to a new veth interface Arguments: vm_name name of the vm to create net_name name of the netwok to arrach to subnet x.x.x.x/len - optional if network already exists netns Network namespace where the veth interface is bound to. Defaults to net_name Returns: A dict with the following elements: port_id uuid of port ip veth name of veth interface netns Network namespace where the veth interface is bound to """ # remember what to clean up if things go wrong port_created = False veth_created = False netns_created = False ip_created = False vmi_created = False vnet_created = False vm_created = False try: # sanity check net_name = self.args.get('net_name') if not net_name: raise ValueError("Network name argument is required") # sanitize netns since it gets passed to the shell netns = self.args.get('netns') if not netns: netns = net_name if not re.match(r'^[-.\w]+$', netns): raise ValueError("netns=[%s] must be a valid namespace name" + " (a single word)" % netns) vnc_client = self.vnc_connect() proj_fq_name = self.args['project'].split(':') # find or create the VM vm_fq_name = proj_fq_name + [ self.args['vm_name'] ] # debug #import pdb; pdb.set_trace() try: vm = vnc_client.virtual_machine_read(fq_name = vm_fq_name) if vm: raise ValueError(("Virtual machine named %s already exists." + " Use --delete to delete it") % self.args['vm_name']) except vnc_api.NoIdError: # create vm if necessary vm = vnc_api.VirtualMachine(':'.join(vm_fq_name), fq_name=vm_fq_name) vnc_client.virtual_machine_create(vm) vm = vnc_client.virtual_machine_read(fq_name = vm_fq_name) vm_created = True # find or create the network vnet_fq_name = proj_fq_name + [ net_name ] vnet_created = False try: vnet = vnc_client.virtual_network_read(fq_name = vnet_fq_name) except vnc_api.NoIdError: # create the network if it doesn't exist vnet = vnc_api.VirtualNetwork(vnet_fq_name[-1], parent_type = 'project', fq_name = vnet_fq_name) # add a subnet ipam = vnc_client.network_ipam_read( fq_name = ['default-domain', 'default-project', 'default-network-ipam']) (prefix, plen) = self.args['subnet'].split('/') subnet = vnc_api.IpamSubnetType( subnet = vnc_api.SubnetType(prefix, int(plen))) vnet.add_network_ipam(ipam, vnc_api.VnSubnetsType([subnet])) vnc_client.virtual_network_create(vnet) vnet_created = True # find or create the vmi vmi_fq_name = vm.fq_name + ['0'] vmi_created = False try: vmi = vnc_client.virtual_machine_interface_read( fq_name = vmi_fq_name) except vnc_api.NoIdError: vmi = vnc_api.VirtualMachineInterface( parent_type = 'virtual-machine', fq_name = vmi_fq_name) vmi_created = True vmi.set_virtual_network(vnet) if vmi_created: vnc_client.virtual_machine_interface_create(vmi) else: vnc_client.virtual_machine_interface_update(vmi) # re-read the vmi to get its mac addresses vmi = vnc_client.virtual_machine_interface_read( fq_name = vmi_fq_name) # create an IP for the VMI if it doesn't already have one ips = vmi.get_instance_ip_back_refs() if not ips: ip = vnc_api.InstanceIp(vm.name + '.0') ip.set_virtual_machine_interface(vmi) ip.set_virtual_network(vnet) ip_created = vnc_client.instance_ip_create(ip) # Create the veth port. Create a veth pair. Put one end # in the VMI port and the other in a network namespace # get the ip, mac, and gateway from the vmi ip_uuid = vmi.get_instance_ip_back_refs()[0]['uuid'] ip = vnc_client.instance_ip_read(id=ip_uuid).instance_ip_address mac = vmi.virtual_machine_interface_mac_addresses.mac_address[0] subnet = vnet.network_ipam_refs[0]['attr'].ipam_subnets[0] gw = subnet.default_gateway dns = gw # KLUDGE - that's the default, but some networks # have other DNS configurations ipnetaddr = netaddr.IPNetwork("%s/%s" % (subnet.subnet.ip_prefix, subnet.subnet.ip_prefix_len)) # set up the veth pair with one part for vrouter and one # for the netns # find a name that's not already used in the default or # netns namespaces link_exists = link_exists_func('', netns) veth_vrouter = new_interface_name(suffix=vnet.uuid, prefix="ve1", exists_func=link_exists) veth_host = new_interface_name(suffix=vnet.uuid, prefix="ve0", exists_func=link_exists) sudo("ip link add %s type veth peer name %s", (veth_vrouter, veth_host)) veth_created = True try: sudo("ip netns add %s", (netns,)) netns_created = True except ProcessExecutionError: pass sudo("ip link set %s netns %s", (veth_host, netns)) sudo("ip netns exec %s ip link set dev %s address %s", (netns, veth_host, mac)) sudo("ip netns exec %s ip address add %s broadcast %s dev %s", (netns, ("%s/%s" % (ip, subnet.subnet.ip_prefix_len)), ipnetaddr.broadcast, veth_host)) sudo("ip netns exec %s ip link set dev %s up", (netns, veth_host)) sudo("ip netns exec %s route add default gw %s dev %s", (netns, gw, veth_host)) sudo("ip link set dev %s up", (veth_vrouter,)) # make a namespace-specific resolv.conf resolv_conf = "/etc/netns/%s/resolv.conf" % netns resolv_conf_body = "nameserver %s\n" % dns sudo("mkdir -p %s", (os.path.dirname(resolv_conf),)) sudo("tee %s", (resolv_conf,), process_input=resolv_conf_body) # finally, create the Contrail port port = instance_service.ttypes.Port( uuid_from_string(vmi.uuid), uuid_from_string(vm.uuid), veth_vrouter, ip, uuid_from_string(vnet.uuid), mac, ) rpc = vrouter_rpc() rpc.AddPort([port]) port_created = True return(dict( port_id = uuid_array_to_str(port.port_id), vm_id = vm.uuid, net_id = vnet.uuid, vmi_id = vmi.uuid, veth = veth_host, netns = netns, ip = ip, mac = mac, gw = gw, dns = dns, netmask = str(ipnetaddr.netmask), broadcast = str(ipnetaddr.broadcast), )) except: # something went wrong, clean up if port_created: rpc.DeletePort(port.port_id) if veth_created: sudo("ip link delete %s", (veth_vrouter,), check_exit_code=False) if netns_created: sudo("ip netns delete %s", (netns,), check_exit_code=False) if ip_created: vnc_client.instance_ip_delete(id=ip_created) if vmi_created: vnc_client.virtual_machine_interface_delete(id=vmi.uuid) if vnet_created: vnc_client.virtual_network_delete(id=vnet.uuid) if vm_created: vnc_client.virtual_machine_delete(id=vm.uuid) raise
def create(self): # remember what to clean up if things go wrong port_created = False veth_created = False netns_created = False ip_created = False vmi_created = False vnet_created = False vm_created = False try: vnc_client = self.vnc_connect() proj_fq_name = self.args['project'].split(':') vm_fq_name = [ self.args['vm_name']] try: vm = vnc_client.virtual_machine_read(fq_name = vm_fq_name) except cfgm_common.exceptions.NoIdError: # create vm if necessary vm = vnc_api.VirtualMachine(self.args['vm_name'], fq_name=vm_fq_name) vnc_client.virtual_machine_create(vm) vm = vnc_client.virtual_machine_read(fq_name = vm_fq_name) vm_created = True # find the network vnet_fq_name = proj_fq_name + [ self.args['net_name'] ] vnet_created = False vnet = vnc_client.virtual_network_read(fq_name = vnet_fq_name) # find or create the vmi vmi_fq_name = proj_fq_name+['web_proxy_vmi'] vmi_created = False try: vmi = vnc_client.virtual_machine_interface_read( fq_name = vmi_fq_name) except cfgm_common.exceptions.NoIdError: vmi = vnc_api.VirtualMachineInterface( parent_type = 'project', fq_name = vmi_fq_name) vmi_created = True vmi.set_virtual_network(vnet) vmi.set_virtual_machine(vm) if vmi_created: vnc_client.virtual_machine_interface_create(vmi) else: vnc_client.virtual_machine_interface_update(vmi) # re-read the vmi to get its mac addresses vmi = vnc_client.virtual_machine_interface_read( fq_name = vmi_fq_name) # create an IP for the VMI if it doesn't already have one ips = vmi.get_instance_ip_back_refs() if not ips: ip = vnc_api.InstanceIp(vm.name + '.0') ip.set_virtual_machine_interface(vmi) ip.set_virtual_network(vnet) ip_created = vnc_client.instance_ip_create(ip) # Create the veth port. Create a veth pair. Put one end # in the VMI port and the other in a network namespace # get the ip, mac, and gateway from the vmi ip_uuid = vmi.get_instance_ip_back_refs()[0]['uuid'] ip = vnc_client.instance_ip_read(id=ip_uuid).instance_ip_address mac = vmi.virtual_machine_interface_mac_addresses.mac_address[0] subnet = vnet.network_ipam_refs[0]['attr'].ipam_subnets[0] gw = subnet.default_gateway dns = gw # KLUDGE - that's the default, but some networks # have other DNS configurations ipnetaddr = netaddr.IPNetwork("%s/%s" % (subnet.subnet.ip_prefix, subnet.subnet.ip_prefix_len)) # set up the veth pair with one part for vrouter and one # for the netns # find a name that's not already used in the default or # netns namespaces netns=self.args['netns'] link_exists = link_exists_func('', netns) veth_vrouter = new_interface_name(suffix=vnet.uuid, prefix="ve1", exists_func=link_exists) veth_host = new_interface_name(suffix=vnet.uuid, prefix="ve0", exists_func=link_exists) os.system("ip link add %s type veth peer name %s"% (veth_vrouter, veth_host)) veth_created = True if os.system("ip netns add %s"%(netns))==0: netns_created = True os.system("ip link set %s netns %s"%(veth_host, netns)) os.system("ip netns exec %s ip link set dev %s address %s"%(netns, veth_host, mac)) os.system("ip netns exec %s ip address add %s broadcast %s dev %s"%(netns, ("%s/%s" % (ip, subnet.subnet.ip_prefix_len)), ipnetaddr.broadcast, veth_host)) os.system("ip netns exec %s ip link set dev %s up"%(netns, veth_host)) os.system("ip netns exec %s route add default gw %s dev %s"%(netns, gw, veth_host)) os.system("ip link set dev %s up"%(veth_vrouter)) # make a namespace-specific resolv.conf resolv_conf = "/etc/netns/%s/resolv.conf" % netns resolv_conf_body = "nameserver %s" % dns os.system("mkdir -p %s"%(os.path.dirname(resolv_conf))) os.system("echo %s > %s"%(resolv_conf_body,resolv_conf)) # finally, create the Contrail port port = instance_service.ttypes.Port( uuid_from_string(vmi.uuid), uuid_from_string(vm.uuid), veth_vrouter, ip, uuid_from_string(vnet.uuid), mac, ) rpc = vrouter_rpc() rpc.AddPort([port]) port_created = True return(dict( port_id = uuid_array_to_str(port.port_id), vm_id = vm.uuid, net_id = vnet.uuid, vmi_id = vmi.uuid, veth = veth_host, netns = netns, ip = ip, mac = mac, gw = gw, dns = dns, netmask = str(ipnetaddr.netmask), broadcast = str(ipnetaddr.broadcast), )) except: # something went wrong, clean up if port_created: rpc.DeletePort(port.port_id) if veth_created: os.system("ip link delete %s"%(veth_vrouter)) if netns_created: os.system("ip netns delete %s"%(netns)) if ip_created: vnc_client.instance_ip_delete(id=ip_created) if vmi_created: vnc_client.virtual_machine_interface_delete(id=vmi.uuid) if vnet_created: vnc_client.virtual_network_delete(id=vnet.uuid) if vm_created: vnc_client.virtual_machine_delete(id=vm.uuid) raise