github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/maas/devices.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package maas
     5  
     6  import (
     7  	"encoding/json"
     8  	"net/url"
     9  	"path"
    10  	"strconv"
    11  
    12  	"github.com/juju/errors"
    13  
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/network"
    16  )
    17  
    18  // TODO(dimitern): The types below should be part of gomaasapi.
    19  // LKK Card: https://canonical.leankit.com/Boards/View/101652562/119310616
    20  
    21  type maasZone struct {
    22  	Name        string `json:"name"`
    23  	Description string `json:"description"`
    24  	ResourceURI string `json:"resource_uri"`
    25  }
    26  
    27  type maasMACAddress struct {
    28  	MACAddress string `json:"mac_address"`
    29  }
    30  
    31  type maasDevice struct {
    32  	SystemID      string           `json:"system_id"`
    33  	Parent        string           `json:"parent"`
    34  	Hostname      string           `json:"hostname"`
    35  	IPAddresses   []string         `json:"ip_addresses"`
    36  	Owner         string           `json:"owner"`
    37  	Zone          maasZone         `json:"zone"`
    38  	MACAddressSet []maasMACAddress `json:"macaddress_set"`
    39  	TagNames      []string         `json:"tag_names"`
    40  	ResourceURI   string           `json:"resource_uri"`
    41  }
    42  
    43  func parseDevice(jsonBytes []byte) (*maasDevice, error) {
    44  	var device maasDevice
    45  	if err := json.Unmarshal(jsonBytes, &device); err != nil {
    46  		return nil, errors.Annotate(err, "parsing device")
    47  	}
    48  	return &device, nil
    49  }
    50  
    51  func getJSONBytes(object json.Marshaler) ([]byte, error) {
    52  	rawBytes, err := object.MarshalJSON()
    53  	if err != nil {
    54  		return nil, errors.Annotate(err, "cannot get JSON bytes")
    55  	}
    56  	return rawBytes, nil
    57  }
    58  
    59  func (env *maasEnviron) createDevice(hostInstanceID instance.Id, primaryMACAddress string) (*maasDevice, error) {
    60  	devicesAPI := env.getMAASClient().GetSubObject("devices")
    61  	params := make(url.Values)
    62  	params.Add("parent", extractSystemId(hostInstanceID))
    63  	params.Add("mac_addresses", primaryMACAddress)
    64  
    65  	result, err := devicesAPI.CallPost("new", params)
    66  	if err != nil {
    67  		return nil, errors.Trace(err)
    68  	}
    69  	deviceJSON, err := getJSONBytes(result)
    70  	if err != nil {
    71  		return nil, errors.Trace(err)
    72  	}
    73  	device, err := parseDevice(deviceJSON)
    74  	if err != nil {
    75  		return nil, errors.Trace(err)
    76  	}
    77  	logger.Debugf("created device: %+v", device)
    78  	return device, nil
    79  }
    80  
    81  func parseInterface(jsonBytes []byte) (*maasInterface, error) {
    82  	var iface maasInterface
    83  	if err := json.Unmarshal(jsonBytes, &iface); err != nil {
    84  		return nil, errors.Annotate(err, "parsing interface")
    85  	}
    86  	return &iface, nil
    87  }
    88  
    89  func (env *maasEnviron) createDeviceInterface(deviceID instance.Id, name, macAddress, vlanID string) (*maasInterface, error) {
    90  	deviceSystemID := extractSystemId(deviceID)
    91  	uri := path.Join("nodes", deviceSystemID, "interfaces")
    92  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
    93  
    94  	params := make(url.Values)
    95  	params.Add("name", name)
    96  	params.Add("mac_address", macAddress)
    97  	params.Add("vlan", vlanID)
    98  
    99  	result, err := interfacesAPI.CallPost("create_physical", params)
   100  	if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  	interfaceJSON, err := getJSONBytes(result)
   104  	if err != nil {
   105  		return nil, errors.Trace(err)
   106  	}
   107  	iface, err := parseInterface(interfaceJSON)
   108  	if err != nil {
   109  		return nil, errors.Trace(err)
   110  	}
   111  	return iface, nil
   112  }
   113  
   114  func (env *maasEnviron) updateDeviceInterface(deviceID instance.Id, interfaceID, name, macAddress, vlanID string) (*maasInterface, error) {
   115  	deviceSystemID := extractSystemId(deviceID)
   116  	uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID)
   117  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   118  
   119  	params := make(url.Values)
   120  	params.Add("name", name)
   121  	params.Add("mac_address", macAddress)
   122  	params.Add("vlan", vlanID)
   123  
   124  	result, err := interfacesAPI.Update(params)
   125  	if err != nil {
   126  		return nil, errors.Trace(err)
   127  	}
   128  	interfaceJSON, err := getJSONBytes(result)
   129  	if err != nil {
   130  		return nil, errors.Trace(err)
   131  	}
   132  	iface, err := parseInterface(interfaceJSON)
   133  	if err != nil {
   134  		return nil, errors.Trace(err)
   135  	}
   136  	return iface, nil
   137  }
   138  
   139  func (env *maasEnviron) linkDeviceInterfaceToSubnet(deviceID instance.Id, interfaceID, subnetID string, mode maasLinkMode) (*maasInterface, error) {
   140  	deviceSystemID := extractSystemId(deviceID)
   141  	uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID)
   142  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   143  
   144  	params := make(url.Values)
   145  	params.Add("mode", string(mode))
   146  	params.Add("subnet", subnetID)
   147  
   148  	result, err := interfacesAPI.CallPost("link_subnet", params)
   149  	if err != nil {
   150  		return nil, errors.Trace(err)
   151  	}
   152  	interfaceJSON, err := getJSONBytes(result)
   153  	if err != nil {
   154  		return nil, errors.Trace(err)
   155  	}
   156  	iface, err := parseInterface(interfaceJSON)
   157  	if err != nil {
   158  		return nil, errors.Trace(err)
   159  	}
   160  	return iface, nil
   161  }
   162  
   163  func (env *maasEnviron) deviceInterfaces(deviceID instance.Id) ([]maasInterface, error) {
   164  	deviceSystemID := extractSystemId(deviceID)
   165  	uri := path.Join("nodes", deviceSystemID, "interfaces")
   166  	interfacesAPI := env.getMAASClient().GetSubObject(uri)
   167  
   168  	result, err := interfacesAPI.CallGet("", nil)
   169  	if err != nil {
   170  		return nil, errors.Trace(err)
   171  	}
   172  	interfacesJSON, err := getJSONBytes(result)
   173  	if err != nil {
   174  		return nil, errors.Trace(err)
   175  	}
   176  	interfaces, err := parseInterfaces(interfacesJSON)
   177  	if err != nil {
   178  		return nil, errors.Trace(err)
   179  	}
   180  	logger.Debugf("device %q interfaces: %+v", deviceSystemID, interfaces)
   181  	return interfaces, nil
   182  
   183  }
   184  
   185  func (env *maasEnviron) deviceInterfaceInfo(deviceID instance.Id, nameToParentName map[string]string) ([]network.InterfaceInfo, error) {
   186  	interfaces, err := env.deviceInterfaces(deviceID)
   187  	if err != nil {
   188  		return nil, errors.Trace(err)
   189  	}
   190  
   191  	interfaceInfo := make([]network.InterfaceInfo, 0, len(interfaces))
   192  	for _, nic := range interfaces {
   193  		nicInfo := network.InterfaceInfo{
   194  			InterfaceName:       nic.Name,
   195  			InterfaceType:       network.EthernetInterface,
   196  			MACAddress:          nic.MACAddress,
   197  			MTU:                 nic.EffectveMTU,
   198  			VLANTag:             nic.VLAN.VID,
   199  			ProviderId:          network.Id(strconv.Itoa(nic.ID)),
   200  			ProviderVLANId:      network.Id(strconv.Itoa(nic.VLAN.ID)),
   201  			Disabled:            !nic.Enabled,
   202  			NoAutoStart:         !nic.Enabled,
   203  			ParentInterfaceName: nameToParentName[nic.Name],
   204  		}
   205  
   206  		if len(nic.Links) == 0 {
   207  			logger.Debugf("device %q interface %q has no links", deviceID, nic.Name)
   208  			interfaceInfo = append(interfaceInfo, nicInfo)
   209  			continue
   210  		}
   211  
   212  		for _, link := range nic.Links {
   213  			switch link.Mode {
   214  			case modeUnknown:
   215  				nicInfo.ConfigType = network.ConfigUnknown
   216  			case modeDHCP:
   217  				nicInfo.ConfigType = network.ConfigDHCP
   218  			case modeStatic, modeLinkUp:
   219  				nicInfo.ConfigType = network.ConfigStatic
   220  			default:
   221  				nicInfo.ConfigType = network.ConfigManual
   222  			}
   223  
   224  			if link.IPAddress == "" {
   225  				logger.Debugf("device %q interface %q has no address", deviceID, nic.Name)
   226  				continue
   227  			}
   228  			if link.Subnet == nil {
   229  				logger.Debugf("device %q interface %q link %d missing subnet", deviceID, nic.Name, link.ID)
   230  				continue
   231  			}
   232  
   233  			nicInfo.CIDR = link.Subnet.CIDR
   234  			nicInfo.Address = network.NewAddressOnSpace(link.Subnet.Space, link.IPAddress)
   235  			nicInfo.ProviderSubnetId = network.Id(strconv.Itoa(link.Subnet.ID))
   236  			nicInfo.ProviderAddressId = network.Id(strconv.Itoa(link.ID))
   237  			if link.Subnet.GatewayIP != "" {
   238  				nicInfo.GatewayAddress = network.NewAddressOnSpace(link.Subnet.Space, link.Subnet.GatewayIP)
   239  			}
   240  			if len(link.Subnet.DNSServers) > 0 {
   241  				nicInfo.DNSServers = network.NewAddressesOnSpace(link.Subnet.Space, link.Subnet.DNSServers...)
   242  			}
   243  
   244  			interfaceInfo = append(interfaceInfo, nicInfo)
   245  		}
   246  	}
   247  	logger.Debugf("device %q has interface info: %+v", deviceID, interfaceInfo)
   248  	return interfaceInfo, nil
   249  }