github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/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 "github.com/juju/gomaasapi" 14 15 "github.com/juju/juju/instance" 16 "github.com/juju/juju/network" 17 ) 18 19 // TODO(dimitern): The types below should be part of gomaasapi. 20 // LKK Card: https://canonical.leankit.com/Boards/View/101652562/119310616 21 22 type maasZone struct { 23 Name string `json:"name"` 24 Description string `json:"description"` 25 ResourceURI string `json:"resource_uri"` 26 } 27 28 type maasMACAddress struct { 29 MACAddress string `json:"mac_address"` 30 } 31 32 type maasDevice struct { 33 SystemID string `json:"system_id"` 34 Parent string `json:"parent"` 35 Hostname string `json:"hostname"` 36 IPAddresses []string `json:"ip_addresses"` 37 Owner string `json:"owner"` 38 Zone maasZone `json:"zone"` 39 MACAddressSet []maasMACAddress `json:"macaddress_set"` 40 TagNames []string `json:"tag_names"` 41 ResourceURI string `json:"resource_uri"` 42 } 43 44 func parseDevice(jsonBytes []byte) (*maasDevice, error) { 45 var device maasDevice 46 if err := json.Unmarshal(jsonBytes, &device); err != nil { 47 return nil, errors.Annotate(err, "parsing device") 48 } 49 return &device, nil 50 } 51 52 func getJSONBytes(object json.Marshaler) ([]byte, error) { 53 rawBytes, err := object.MarshalJSON() 54 if err != nil { 55 return nil, errors.Annotate(err, "cannot get JSON bytes") 56 } 57 return rawBytes, nil 58 } 59 60 func (env *maasEnviron) createDevice(hostInstanceID instance.Id, primaryMACAddress string) (*maasDevice, error) { 61 devicesAPI := env.getMAASClient().GetSubObject("devices") 62 params := make(url.Values) 63 params.Add("parent", extractSystemId(hostInstanceID)) 64 params.Add("mac_addresses", primaryMACAddress) 65 66 result, err := devicesAPI.CallPost("new", params) 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 deviceJSON, err := getJSONBytes(result) 71 if err != nil { 72 return nil, errors.Trace(err) 73 } 74 device, err := parseDevice(deviceJSON) 75 if err != nil { 76 return nil, errors.Trace(err) 77 } 78 logger.Debugf("created device: %+v", device) 79 return device, nil 80 } 81 82 func parseInterface(jsonBytes []byte) (*maasInterface, error) { 83 var iface maasInterface 84 if err := json.Unmarshal(jsonBytes, &iface); err != nil { 85 return nil, errors.Annotate(err, "parsing interface") 86 } 87 return &iface, nil 88 } 89 90 func (env *maasEnviron) createDeviceInterface(deviceID instance.Id, name, macAddress, vlanID string) (*maasInterface, error) { 91 deviceSystemID := extractSystemId(deviceID) 92 uri := path.Join("nodes", deviceSystemID, "interfaces") 93 interfacesAPI := env.getMAASClient().GetSubObject(uri) 94 95 params := make(url.Values) 96 params.Add("name", name) 97 params.Add("mac_address", macAddress) 98 params.Add("vlan", vlanID) 99 100 result, err := interfacesAPI.CallPost("create_physical", params) 101 if err != nil { 102 return nil, errors.Trace(err) 103 } 104 interfaceJSON, err := getJSONBytes(result) 105 if err != nil { 106 return nil, errors.Trace(err) 107 } 108 iface, err := parseInterface(interfaceJSON) 109 if err != nil { 110 return nil, errors.Trace(err) 111 } 112 return iface, nil 113 } 114 115 func (env *maasEnviron) updateDeviceInterface(deviceID instance.Id, interfaceID, name, macAddress, vlanID string) (*maasInterface, error) { 116 deviceSystemID := extractSystemId(deviceID) 117 uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID) 118 interfacesAPI := env.getMAASClient().GetSubObject(uri) 119 120 params := make(url.Values) 121 params.Add("name", name) 122 params.Add("mac_address", macAddress) 123 params.Add("vlan", vlanID) 124 125 result, err := interfacesAPI.Update(params) 126 if err != nil { 127 return nil, errors.Trace(err) 128 } 129 interfaceJSON, err := getJSONBytes(result) 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 iface, err := parseInterface(interfaceJSON) 134 if err != nil { 135 return nil, errors.Trace(err) 136 } 137 return iface, nil 138 } 139 140 func (env *maasEnviron) linkDeviceInterfaceToSubnet(deviceID instance.Id, interfaceID, subnetID string, mode maasLinkMode) (*maasInterface, error) { 141 deviceSystemID := extractSystemId(deviceID) 142 uri := path.Join("nodes", deviceSystemID, "interfaces", interfaceID) 143 interfacesAPI := env.getMAASClient().GetSubObject(uri) 144 145 params := make(url.Values) 146 params.Add("mode", string(mode)) 147 params.Add("subnet", subnetID) 148 149 result, err := interfacesAPI.CallPost("link_subnet", params) 150 if err != nil { 151 return nil, errors.Trace(err) 152 } 153 interfaceJSON, err := getJSONBytes(result) 154 if err != nil { 155 return nil, errors.Trace(err) 156 } 157 iface, err := parseInterface(interfaceJSON) 158 if err != nil { 159 return nil, errors.Trace(err) 160 } 161 return iface, nil 162 } 163 164 func (env *maasEnviron) deviceInterfaces(deviceID instance.Id) ([]maasInterface, error) { 165 deviceSystemID := extractSystemId(deviceID) 166 uri := path.Join("nodes", deviceSystemID, "interfaces") 167 interfacesAPI := env.getMAASClient().GetSubObject(uri) 168 169 result, err := interfacesAPI.CallGet("", nil) 170 if err != nil { 171 return nil, errors.Trace(err) 172 } 173 interfacesJSON, err := getJSONBytes(result) 174 if err != nil { 175 return nil, errors.Trace(err) 176 } 177 interfaces, err := parseInterfaces(interfacesJSON) 178 if err != nil { 179 return nil, errors.Trace(err) 180 } 181 logger.Debugf("device %q interfaces: %+v", deviceSystemID, interfaces) 182 return interfaces, nil 183 184 } 185 186 func (env *maasEnviron) deviceInterfaceInfo(deviceID instance.Id, nameToParentName map[string]string) ([]network.InterfaceInfo, error) { 187 interfaces, err := env.deviceInterfaces(deviceID) 188 if err != nil { 189 return nil, errors.Trace(err) 190 } 191 192 interfaceInfo := make([]network.InterfaceInfo, 0, len(interfaces)) 193 for _, nic := range interfaces { 194 nicInfo := network.InterfaceInfo{ 195 InterfaceName: nic.Name, 196 InterfaceType: network.EthernetInterface, 197 MACAddress: nic.MACAddress, 198 MTU: nic.EffectveMTU, 199 VLANTag: nic.VLAN.VID, 200 ProviderId: network.Id(strconv.Itoa(nic.ID)), 201 ProviderVLANId: network.Id(strconv.Itoa(nic.VLAN.ID)), 202 Disabled: !nic.Enabled, 203 NoAutoStart: !nic.Enabled, 204 ParentInterfaceName: nameToParentName[nic.Name], 205 } 206 207 if len(nic.Links) == 0 { 208 logger.Debugf("device %q interface %q has no links", deviceID, nic.Name) 209 interfaceInfo = append(interfaceInfo, nicInfo) 210 continue 211 } 212 213 for _, link := range nic.Links { 214 switch link.Mode { 215 case modeUnknown: 216 nicInfo.ConfigType = network.ConfigUnknown 217 case modeDHCP: 218 nicInfo.ConfigType = network.ConfigDHCP 219 case modeStatic, modeLinkUp: 220 nicInfo.ConfigType = network.ConfigStatic 221 default: 222 nicInfo.ConfigType = network.ConfigManual 223 } 224 225 if link.IPAddress == "" { 226 logger.Debugf("device %q interface %q has no address", deviceID, nic.Name) 227 continue 228 } 229 if link.Subnet == nil { 230 logger.Debugf("device %q interface %q link %d missing subnet", deviceID, nic.Name, link.ID) 231 continue 232 } 233 234 nicInfo.CIDR = link.Subnet.CIDR 235 nicInfo.Address = network.NewAddressOnSpace(link.Subnet.Space, link.IPAddress) 236 nicInfo.ProviderSubnetId = network.Id(strconv.Itoa(link.Subnet.ID)) 237 nicInfo.ProviderAddressId = network.Id(strconv.Itoa(link.ID)) 238 if link.Subnet.GatewayIP != "" { 239 nicInfo.GatewayAddress = network.NewAddressOnSpace(link.Subnet.Space, link.Subnet.GatewayIP) 240 } 241 if len(link.Subnet.DNSServers) > 0 { 242 nicInfo.DNSServers = network.NewAddressesOnSpace(link.Subnet.Space, link.Subnet.DNSServers...) 243 } 244 245 interfaceInfo = append(interfaceInfo, nicInfo) 246 } 247 } 248 logger.Debugf("device %q has interface info: %+v", deviceID, interfaceInfo) 249 return interfaceInfo, nil 250 } 251 252 func (env *maasEnviron) deviceInterfaceInfo2(deviceID string, nameToParentName map[string]string) ([]network.InterfaceInfo, error) { 253 args := gomaasapi.DevicesArgs{SystemIDs: []string{deviceID}} 254 devices, err := env.maasController.Devices(args) 255 if err != nil { 256 return nil, errors.Trace(err) 257 } 258 if len(devices) != 1 { 259 return nil, errors.Errorf("unexpected response requesting device %v: %v", deviceID, devices) 260 } 261 interfaces := devices[0].InterfaceSet() 262 263 interfaceInfo := make([]network.InterfaceInfo, 0, len(interfaces)) 264 for _, nic := range interfaces { 265 vlanId := 0 266 vlanVid := 0 267 vlan := nic.VLAN() 268 if vlan != nil { 269 vlanId = vlan.ID() 270 vlanVid = vlan.VID() 271 } 272 nicInfo := network.InterfaceInfo{ 273 InterfaceName: nic.Name(), 274 InterfaceType: network.EthernetInterface, 275 MACAddress: nic.MACAddress(), 276 MTU: nic.EffectiveMTU(), 277 VLANTag: vlanVid, 278 ProviderId: network.Id(strconv.Itoa(nic.ID())), 279 ProviderVLANId: network.Id(strconv.Itoa(vlanId)), 280 Disabled: !nic.Enabled(), 281 NoAutoStart: !nic.Enabled(), 282 ParentInterfaceName: nameToParentName[nic.Name()], 283 } 284 285 if len(nic.Links()) == 0 { 286 logger.Debugf("device %q interface %q has no links", deviceID, nic.Name()) 287 interfaceInfo = append(interfaceInfo, nicInfo) 288 continue 289 } 290 291 for _, link := range nic.Links() { 292 mode := maasLinkMode(link.Mode()) 293 switch mode { 294 case modeUnknown: 295 nicInfo.ConfigType = network.ConfigUnknown 296 case modeDHCP: 297 nicInfo.ConfigType = network.ConfigDHCP 298 case modeStatic, modeLinkUp: 299 nicInfo.ConfigType = network.ConfigStatic 300 default: 301 nicInfo.ConfigType = network.ConfigManual 302 } 303 304 subnet := link.Subnet() 305 if link.IPAddress() == "" || subnet == nil { 306 logger.Debugf("device %q interface %q has no address", deviceID, nic.Name()) 307 continue 308 } 309 310 nicInfo.CIDR = subnet.CIDR() 311 nicInfo.Address = network.NewAddressOnSpace(subnet.Space(), link.IPAddress()) 312 nicInfo.ProviderSubnetId = network.Id(strconv.Itoa(subnet.ID())) 313 nicInfo.ProviderAddressId = network.Id(strconv.Itoa(link.ID())) 314 if subnet.Gateway() != "" { 315 nicInfo.GatewayAddress = network.NewAddressOnSpace(subnet.Space(), subnet.Gateway()) 316 } 317 if len(subnet.DNSServers()) > 0 { 318 nicInfo.DNSServers = network.NewAddressesOnSpace(subnet.Space(), subnet.DNSServers()...) 319 } 320 321 interfaceInfo = append(interfaceInfo, nicInfo) 322 } 323 } 324 logger.Debugf("device %q has interface info: %+v", deviceID, interfaceInfo) 325 return interfaceInfo, nil 326 }