Exemple #1
0
// ManipulateFiles exercises the /api/1.0/nodes/ API endpoint.  Most precisely,
// it lists the existing nodes, creates a new node, updates it and then
// deletes it.
func ManipulateNodes(maas *gomaasapi.MAASObject) {
	nodeListing := maas.GetSubObject("nodes")

	// List nodes.
	fmt.Println("Fetching list of nodes...")
	listNodeObjects, err := nodeListing.CallGet("list", url.Values{})
	checkError(err)
	listNodes, err := listNodeObjects.GetArray()
	checkError(err)
	fmt.Printf("Got list of %v nodes\n", len(listNodes))
	for index, nodeObj := range listNodes {
		node, err := nodeObj.GetMAASObject()
		checkError(err)
		hostname, err := node.GetField("hostname")
		checkError(err)
		fmt.Printf("Node #%d is named '%v' (%v)\n", index, hostname, node.URL())
	}

	// Create a node.
	fmt.Println("Creating a new node...")
	params := url.Values{"architecture": {"i386/generic"}, "mac_addresses": {"AA:BB:CC:DD:EE:FF"}}
	newNodeObj, err := nodeListing.CallPost("new", params)
	checkError(err)
	newNode, err := newNodeObj.GetMAASObject()
	checkError(err)
	newNodeName, err := newNode.GetField("hostname")
	checkError(err)
	fmt.Printf("New node created: %s (%s)\n", newNodeName, newNode.URL())

	// Update the new node.
	fmt.Println("Updating the new node...")
	updateParams := url.Values{"hostname": {"mynewname"}}
	newNodeObj2, err := newNode.Update(updateParams)
	checkError(err)
	newNodeName2, err := newNodeObj2.GetField("hostname")
	checkError(err)
	fmt.Printf("New node updated, now named: %s\n", newNodeName2)

	// Count the nodes.
	listNodeObjects2, err := nodeListing.CallGet("list", url.Values{})
	checkError(err)
	listNodes2, err := listNodeObjects2.GetArray()
	checkError(err)
	fmt.Printf("We've got %v nodes\n", len(listNodes2))

	// Delete the new node.
	fmt.Println("Deleting the new node...")
	errDelete := newNode.Delete()
	checkError(errDelete)

	// Count the nodes.
	listNodeObjects3, err := nodeListing.CallGet("list", url.Values{})
	checkError(err)
	listNodes3, err := listNodeObjects3.GetArray()
	checkError(err)
	fmt.Printf("We've got %v nodes\n", len(listNodes3))
}
Exemple #2
0
// ManipulateFiles exercises the /api/1.0/files/ API endpoint.  Most precisely,
// it uploads a files and then fetches it, making sure the received content
// is the same as the one that was sent.
func ManipulateFiles(maas *gomaasapi.MAASObject) {
	files := maas.GetSubObject("files")
	fileContent := []byte("test file content")
	fileName := "filename"
	filesToUpload := map[string][]byte{"file": fileContent}

	// Upload a file.
	fmt.Println("Uploading a file...")
	_, err := files.CallPostFiles("add", url.Values{"filename": {fileName}}, filesToUpload)
	checkError(err)
	fmt.Println("File sent.")

	// Fetch the file.
	fmt.Println("Fetching the file...")
	fileResult, err := files.CallGet("get", url.Values{"filename": {fileName}})
	checkError(err)
	receivedFileContent, err := fileResult.GetBytes()
	checkError(err)
	if bytes.Compare(receivedFileContent, fileContent) != 0 {
		panic("Received content differs from the content sent!")
	}
	fmt.Println("Got file.")

	// Fetch list of files.
	listFiles, err := files.CallGet("list", url.Values{})
	checkError(err)
	listFilesArray, err := listFiles.GetArray()
	checkError(err)
	fmt.Printf("We've got %v file(s)\n", len(listFilesArray))

	// Delete the file.
	fmt.Println("Deleting the file...")
	fileObject, err := listFilesArray[0].GetMAASObject()
	checkError(err)
	errDelete := fileObject.Delete()
	checkError(errDelete)

	// Count the files.
	listFiles, err = files.CallGet("list", url.Values{})
	checkError(err)
	listFilesArray, err = listFiles.GetArray()
	checkError(err)
	fmt.Printf("We've got %v file(s)\n", len(listFilesArray))
}
// fetchNodes do a HTTP GET to the MAAS server to query all the nodes
func fetchNodes(client *maas.MAASObject) ([]MaasNode, error) {
	nodeListing := client.GetSubObject("nodes")
	listNodeObjects, err := nodeListing.CallGet("list", url.Values{})
	if checkWarn(err, "unable to get the list of all nodes: %s", err) {
		return nil, err
	}
	listNodes, err := listNodeObjects.GetArray()
	if checkWarn(err, "unable to get the node objects for the list: %s", err) {
		return nil, err
	}

	var nodes = make([]MaasNode, len(listNodes))
	for index, nodeObj := range listNodes {
		node, err := nodeObj.GetMAASObject()
		if !checkWarn(err, "unable to retrieve object for node: %s", err) {
			nodes[index] = MaasNode{node}
		}
	}
	return nodes, nil
}
Exemple #4
0
// updateName - changes the name of the MAAS node based on the configuration file
func updateNodeName(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
	macs := node.MACs()

	// Get current node name and strip off domain name
	current := node.Hostname()
	if i := strings.IndexRune(current, '.'); i != -1 {
		current = current[:i]
	}
	for _, mac := range macs {
		if entry, ok := options.Mappings[mac]; ok {
			if name, ok := entry.(map[string]interface{})["hostname"]; ok && current != name.(string) {
				nodesObj := client.GetSubObject("nodes")
				nodeObj := nodesObj.GetSubObject(node.ID())
				log.Printf("RENAME '%s' to '%s'\n", node.Hostname(), name.(string))

				if !options.Preview {
					nodeObj.Update(url.Values{"hostname": []string{name.(string)}})
				}
			}
		}
	}
	return nil
}
Exemple #5
0
func maasObjectId(maasObject *gomaasapi.MAASObject) instance.Id {
	// Use the node's 'resource_uri' value.
	return instance.Id(maasObject.URI().String())
}
Exemple #6
0
// maasObjectNetworkInterfaces implements environs.NetworkInterfaces() using the
// new (1.9+) MAAS API, parsing the node details JSON embedded into the given
// maasObject to extract all the relevant InterfaceInfo fields. It returns an
// error satisfying errors.IsNotSupported() if it cannot find the required
// "interface_set" node details field.
func maasObjectNetworkInterfaces(maasObject *gomaasapi.MAASObject, subnetsMap map[string]network.Id) ([]network.InterfaceInfo, error) {
	interfaceSet, ok := maasObject.GetMap()["interface_set"]
	if !ok || interfaceSet.IsNil() {
		// This means we're using an older MAAS API.
		return nil, errors.NotSupportedf("interface_set")
	}

	// TODO(dimitern): Change gomaasapi JSONObject to give access to the raw
	// JSON bytes directly, rather than having to do call MarshalJSON just so
	// the result can be unmarshaled from it.
	//
	// LKK Card: https://canonical.leankit.com/Boards/View/101652562/119311323

	rawBytes, err := interfaceSet.MarshalJSON()
	if err != nil {
		return nil, errors.Annotate(err, "cannot get interface_set JSON bytes")
	}

	interfaces, err := parseInterfaces(rawBytes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	infos := make([]network.InterfaceInfo, 0, len(interfaces))
	for i, iface := range interfaces {

		// The below works for all types except bonds and their members.
		parentName := strings.Join(iface.Parents, "")
		var nicType network.InterfaceType
		switch iface.Type {
		case typePhysical:
			nicType = network.EthernetInterface
			children := strings.Join(iface.Children, "")
			if parentName == "" && len(iface.Children) == 1 && strings.HasPrefix(children, "bond") {
				// FIXME: Verify the bond exists, regardless of its name.
				// This is a bond member, set the parent correctly (from
				// Juju's perspective) - to the bond itself.
				parentName = children
			}
		case typeBond:
			parentName = ""
			nicType = network.BondInterface
		case typeVLAN:
			nicType = network.VLAN_8021QInterface
		}

		nicInfo := network.InterfaceInfo{
			DeviceIndex:         i,
			MACAddress:          iface.MACAddress,
			ProviderId:          network.Id(fmt.Sprintf("%v", iface.ID)),
			VLANTag:             iface.VLAN.VID,
			InterfaceName:       iface.Name,
			InterfaceType:       nicType,
			ParentInterfaceName: parentName,
			Disabled:            !iface.Enabled,
			NoAutoStart:         !iface.Enabled,
		}

		for _, link := range iface.Links {
			switch link.Mode {
			case modeUnknown:
				nicInfo.ConfigType = network.ConfigUnknown
			case modeDHCP:
				nicInfo.ConfigType = network.ConfigDHCP
			case modeStatic, modeLinkUp:
				nicInfo.ConfigType = network.ConfigStatic
			default:
				nicInfo.ConfigType = network.ConfigManual
			}

			if link.IPAddress == "" {
				logger.Debugf("interface %q has no address", iface.Name)
			} else {
				// We set it here initially without a space, just so we don't
				// lose it when we have no linked subnet below.
				nicInfo.Address = network.NewAddress(link.IPAddress)
				nicInfo.ProviderAddressId = network.Id(fmt.Sprintf("%v", link.ID))
			}

			if link.Subnet == nil {
				logger.Debugf("interface %q link %d missing subnet", iface.Name, link.ID)
				infos = append(infos, nicInfo)
				continue
			}

			sub := link.Subnet
			nicInfo.CIDR = sub.CIDR
			nicInfo.ProviderSubnetId = network.Id(fmt.Sprintf("%v", sub.ID))
			nicInfo.ProviderVLANId = network.Id(fmt.Sprintf("%v", sub.VLAN.ID))

			// Now we know the subnet and space, we can update the address to
			// store the space with it.
			nicInfo.Address = network.NewAddressOnSpace(sub.Space, link.IPAddress)
			spaceId, ok := subnetsMap[string(sub.CIDR)]
			if !ok {
				// The space we found is not recognised, no
				// provider id available.
				logger.Warningf("interface %q link %d has unrecognised space %q", iface.Name, link.ID, sub.Space)
			} else {
				nicInfo.Address.SpaceProviderId = spaceId
				nicInfo.ProviderSpaceId = spaceId
			}

			gwAddr := network.NewAddressOnSpace(sub.Space, sub.GatewayIP)
			nicInfo.DNSServers = network.NewAddressesOnSpace(sub.Space, sub.DNSServers...)
			if ok {
				gwAddr.SpaceProviderId = spaceId
				for i := range nicInfo.DNSServers {
					nicInfo.DNSServers[i].SpaceProviderId = spaceId
				}
			}
			nicInfo.GatewayAddress = gwAddr
			nicInfo.MTU = sub.VLAN.MTU

			// Each link we represent as a separate InterfaceInfo, but with the
			// same name and device index, just different addres, subnet, etc.
			infos = append(infos, nicInfo)
		}
	}
	return infos, nil
}
Exemple #7
0
// maasObjectNetworkInterfaces implements environs.NetworkInterfaces() using the
// new (1.9+) MAAS API, parsing the node details JSON embedded into the given
// maasObject to extract all the relevant InterfaceInfo fields. It returns an
// error satisfying errors.IsNotSupported() if it cannot find the required
// "interface_set" node details field.
func maasObjectNetworkInterfaces(maasObject *gomaasapi.MAASObject) ([]network.InterfaceInfo, error) {

	interfaceSet, ok := maasObject.GetMap()["interface_set"]
	if !ok || interfaceSet.IsNil() {
		// This means we're using an older MAAS API.
		return nil, errors.NotSupportedf("interface_set")
	}

	// TODO(dimitern): Change gomaasapi JSONObject to give access to the raw
	// JSON bytes directly, rather than having to do call MarshalJSON just so
	// the result can be unmarshaled from it.
	//
	// LKK Card: https://canonical.leankit.com/Boards/View/101652562/119311323

	rawBytes, err := interfaceSet.MarshalJSON()
	if err != nil {
		return nil, errors.Annotate(err, "cannot get interface_set JSON bytes")
	}

	interfaces, err := parseInterfaces(rawBytes)
	if err != nil {
		return nil, errors.Trace(err)
	}

	infos := make([]network.InterfaceInfo, 0, len(interfaces))
	for i, iface := range interfaces {
		nicInfo := network.InterfaceInfo{
			DeviceIndex:   i,
			MACAddress:    iface.MACAddress,
			ProviderId:    network.Id(fmt.Sprintf("%v", iface.ID)),
			VLANTag:       iface.VLAN.VID,
			InterfaceName: iface.Name,
			Disabled:      !iface.Enabled,
			NoAutoStart:   !iface.Enabled,
			// This is not needed anymore, but the provisioner still validates it's set.
			NetworkName: network.DefaultPrivate,
		}

		for _, link := range iface.Links {
			switch link.Mode {
			case modeUnknown:
				nicInfo.ConfigType = network.ConfigUnknown
			case modeDHCP:
				nicInfo.ConfigType = network.ConfigDHCP
			case modeStatic, modeLinkUp:
				nicInfo.ConfigType = network.ConfigStatic
			default:
				nicInfo.ConfigType = network.ConfigManual
			}

			if link.IPAddress == "" {
				logger.Warningf("interface %q has no address", iface.Name)
			} else {
				// We set it here initially without a space, just so we don't
				// lose it when we have no linked subnet below.
				nicInfo.Address = network.NewAddress(link.IPAddress)
			}

			if link.Subnet == nil {
				logger.Warningf("interface %q link %d missing subnet", iface.Name, link.ID)
				infos = append(infos, nicInfo)
				continue
			}

			sub := link.Subnet
			nicInfo.CIDR = sub.CIDR
			nicInfo.ProviderSubnetId = network.Id(fmt.Sprintf("%v", sub.ID))

			// Now we know the subnet and space, we can update the address to
			// store the space with it.
			nicInfo.Address = network.NewAddressOnSpace(sub.Space, link.IPAddress)

			gwAddr := network.NewAddressOnSpace(sub.Space, sub.GatewayIP)
			nicInfo.GatewayAddress = gwAddr

			nicInfo.DNSServers = network.NewAddressesOnSpace(sub.Space, sub.DNSServers...)
			nicInfo.MTU = sub.VLAN.MTU

			// Each link we represent as a separate InterfaceInfo, but with the
			// same name and device index, just different addres, subnet, etc.
			infos = append(infos, nicInfo)
		}
	}
	return infos, nil
}