github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/maas/instance.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  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/gomaasapi/v2"
    12  
    13  	"github.com/juju/juju/core/instance"
    14  	corenetwork "github.com/juju/juju/core/network"
    15  	"github.com/juju/juju/core/network/firewall"
    16  	"github.com/juju/juju/core/status"
    17  	"github.com/juju/juju/environs/context"
    18  )
    19  
    20  type maasInstance struct {
    21  	machine           gomaasapi.Machine
    22  	constraintMatches gomaasapi.ConstraintMatches
    23  	environ           *maasEnviron
    24  }
    25  
    26  func (mi *maasInstance) zone() (string, error) {
    27  	return mi.machine.Zone().Name(), nil
    28  }
    29  
    30  func (mi *maasInstance) hostname() (string, error) {
    31  	return mi.machine.Hostname(), nil
    32  }
    33  
    34  func (mi *maasInstance) hardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
    35  	nodeArch := strings.Split(mi.machine.Architecture(), "/")[0]
    36  	nodeCpuCount := uint64(mi.machine.CPUCount())
    37  	nodeMemoryMB := uint64(mi.machine.Memory())
    38  	// zone can't error on the maasInstance implementation.
    39  	zone, _ := mi.zone()
    40  	tags := mi.machine.Tags()
    41  	hc := &instance.HardwareCharacteristics{
    42  		Arch:             &nodeArch,
    43  		CpuCores:         &nodeCpuCount,
    44  		Mem:              &nodeMemoryMB,
    45  		AvailabilityZone: &zone,
    46  		Tags:             &tags,
    47  	}
    48  	return hc, nil
    49  }
    50  
    51  func (mi *maasInstance) displayName() (string, error) {
    52  	hostname := mi.machine.Hostname()
    53  	if hostname != "" {
    54  		return hostname, nil
    55  	}
    56  	return mi.machine.FQDN(), nil
    57  }
    58  
    59  func (mi *maasInstance) String() string {
    60  	return fmt.Sprintf("%s:%s", mi.machine.Hostname(), mi.machine.SystemID())
    61  }
    62  
    63  func (mi *maasInstance) Id() instance.Id {
    64  	return instance.Id(mi.machine.SystemID())
    65  }
    66  
    67  func (mi *maasInstance) Addresses(ctx context.ProviderCallContext) (corenetwork.ProviderAddresses, error) {
    68  	subnetsMap, err := mi.environ.subnetToSpaceIds(ctx)
    69  	if err != nil {
    70  		return nil, errors.Trace(err)
    71  	}
    72  	// Get all the interface details and extract the addresses.
    73  	interfaces, err := maasNetworkInterfaces(ctx, mi, subnetsMap)
    74  
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  
    79  	var addresses []corenetwork.ProviderAddress
    80  	for _, iface := range interfaces {
    81  		if primAddr := iface.PrimaryAddress(); primAddr.Value != "" {
    82  			addresses = append(addresses, primAddr)
    83  		} else {
    84  			logger.Debugf("no address found on interface %q", iface.InterfaceName)
    85  		}
    86  	}
    87  
    88  	logger.Debugf("%q has addresses %q", mi.machine.Hostname(), addresses)
    89  	return addresses, nil
    90  }
    91  
    92  // Status returns a juju status based on the maas instance returned
    93  // status message.
    94  func (mi *maasInstance) Status(ctx context.ProviderCallContext) instance.Status {
    95  	// A fresh status is not obtained here because the interface it is intended
    96  	// to satisfy gets a new maasInstance before each call, using a fresh status
    97  	// would cause us to mask errors since this interface does not contemplate
    98  	// returning them.
    99  	statusName := mi.machine.StatusName()
   100  	statusMsg := mi.machine.StatusMessage()
   101  	return convertInstanceStatus(statusName, statusMsg, mi.Id())
   102  }
   103  
   104  func convertInstanceStatus(statusMsg, substatus string, id instance.Id) instance.Status {
   105  	maasInstanceStatus := status.Empty
   106  	switch normalizeStatus(statusMsg) {
   107  	case "":
   108  		logger.Debugf("unable to obtain status of instance %s", id)
   109  		statusMsg = "error in getting status"
   110  	case "deployed":
   111  		maasInstanceStatus = status.Running
   112  	case "deploying":
   113  		maasInstanceStatus = status.Allocating
   114  		if substatus != "" {
   115  			statusMsg = fmt.Sprintf("%s: %s", statusMsg, substatus)
   116  		}
   117  	case "failed deployment":
   118  		maasInstanceStatus = status.ProvisioningError
   119  		if substatus != "" {
   120  			statusMsg = fmt.Sprintf("%s: %s", statusMsg, substatus)
   121  		}
   122  	default:
   123  		maasInstanceStatus = status.Empty
   124  		statusMsg = fmt.Sprintf("%s: %s", statusMsg, substatus)
   125  	}
   126  	return instance.Status{
   127  		Status:  maasInstanceStatus,
   128  		Message: statusMsg,
   129  	}
   130  }
   131  
   132  func normalizeStatus(statusMsg string) string {
   133  	return strings.ToLower(strings.TrimSpace(statusMsg))
   134  }
   135  
   136  // MAAS does not do firewalling so these port methods do nothing.
   137  
   138  func (mi *maasInstance) OpenPorts(_ context.ProviderCallContext, _ string, _ firewall.IngressRules) error {
   139  	logger.Debugf("unimplemented OpenPorts() called")
   140  	return nil
   141  }
   142  
   143  func (mi *maasInstance) ClosePorts(_ context.ProviderCallContext, _ string, _ firewall.IngressRules) error {
   144  	logger.Debugf("unimplemented ClosePorts() called")
   145  	return nil
   146  }
   147  
   148  func (mi *maasInstance) IngressRules(_ context.ProviderCallContext, _ string) (firewall.IngressRules, error) {
   149  	logger.Debugf("unimplemented IngressRules() called")
   150  	return nil, nil
   151  }