github.com/Cloud-Foundations/Dominator@v0.3.4/fleetmanager/hypervisors/listHypervisors.go (about)

     1  package hypervisors
     2  
     3  import (
     4  	"bufio"
     5  	"fmt"
     6  	"net/http"
     7  	"sort"
     8  
     9  	"github.com/Cloud-Foundations/Dominator/lib/constants"
    10  	"github.com/Cloud-Foundations/Dominator/lib/format"
    11  	"github.com/Cloud-Foundations/Dominator/lib/html"
    12  	"github.com/Cloud-Foundations/Dominator/lib/json"
    13  	"github.com/Cloud-Foundations/Dominator/lib/tags"
    14  	"github.com/Cloud-Foundations/Dominator/lib/tags/tagmatcher"
    15  	"github.com/Cloud-Foundations/Dominator/lib/url"
    16  	proto "github.com/Cloud-Foundations/Dominator/proto/fleetmanager"
    17  )
    18  
    19  const (
    20  	showOK = iota
    21  	showConnected
    22  	showDisabled
    23  	showAll
    24  	showOff
    25  )
    26  
    27  type hypervisorList []*hypervisorType
    28  
    29  func writeHypervisorTotalsStats(hypervisors []*hypervisorType, location string,
    30  	numVMs uint, tw *html.TableWriter) {
    31  	var memoryInMiBAllocated, memoryInMiBTotal uint64
    32  	var milliCPUsAllocated uint64
    33  	var cpusTotal uint
    34  	var volumeBytesAllocated, volumeBytesTotal uint64
    35  	for _, h := range hypervisors {
    36  		memoryInMiBAllocated += h.allocatedMemory
    37  		memoryInMiBTotal += h.memoryInMiB
    38  		milliCPUsAllocated += h.allocatedMilliCPUs
    39  		cpusTotal += h.numCPUs
    40  		volumeBytesAllocated += h.allocatedVolumeBytes
    41  		volumeBytesTotal += h.totalVolumeBytes
    42  	}
    43  	memoryShift, memoryMultiplier := format.GetMiltiplier(
    44  		memoryInMiBAllocated << 20)
    45  	volumeShift, volumeMultiplier := format.GetMiltiplier(
    46  		volumeBytesAllocated)
    47  	var vmsString string
    48  	if location == "" {
    49  		vmsString = fmt.Sprintf("<a href=\"listVMs\">%d</a>", numVMs)
    50  	} else {
    51  		vmsString = fmt.Sprintf("<a href=\"listVMs?location=%s\">%d</a>",
    52  			location, numVMs)
    53  	}
    54  	tw.WriteRow("", "",
    55  		"<b>TOTAL</b>",
    56  		"",
    57  		"",
    58  		"",
    59  		"",
    60  		"",
    61  		fmt.Sprintf("%s/%d", format.FormatMilli(milliCPUsAllocated), cpusTotal),
    62  		fmt.Sprintf("%d/%d %sB",
    63  			memoryInMiBAllocated<<20>>memoryShift,
    64  			memoryInMiBTotal<<20>>memoryShift,
    65  			memoryMultiplier),
    66  		fmt.Sprintf("%d/%d %sB",
    67  			volumeBytesAllocated>>volumeShift,
    68  			volumeBytesTotal>>volumeShift,
    69  			volumeMultiplier),
    70  		vmsString)
    71  	tw.WriteRow("", "",
    72  		"<b>USAGE</b>",
    73  		"",
    74  		"",
    75  		"",
    76  		"",
    77  		"",
    78  		fmt.Sprintf("%d%%", milliCPUsAllocated/uint64(cpusTotal)/10),
    79  		fmt.Sprintf("%d%%", memoryInMiBAllocated*100/memoryInMiBTotal),
    80  		fmt.Sprintf("%d%%", volumeBytesAllocated*100/volumeBytesTotal),
    81  		"")
    82  }
    83  
    84  func (h *hypervisorType) getHealthStatus() string {
    85  	healthStatus := h.probeStatus.String()
    86  	if h.probeStatus == probeStatusConnected {
    87  		if h.healthStatus != "" {
    88  			healthStatus = h.healthStatus
    89  		} else if h.disabled {
    90  			healthStatus = "disabled"
    91  		}
    92  	}
    93  	return healthStatus
    94  }
    95  
    96  func (h *hypervisorType) getNumVMs() uint {
    97  	h.mutex.RLock()
    98  	defer h.mutex.RUnlock()
    99  	return uint(len(h.vms))
   100  }
   101  
   102  func (h *hypervisorType) writeStats(tw *html.TableWriter) uint {
   103  	machine := h.machine
   104  	machineType := machine.Tags["Type"]
   105  	if machineTypeURL := machine.Tags["TypeURL"]; machineTypeURL != "" {
   106  		machineType = `<a href="` + machineTypeURL + `">` + machineType +
   107  			`</a>`
   108  	}
   109  	numShift := 0
   110  	memoryInMiB := h.memoryInMiB
   111  	for ; memoryInMiB >= 16; numShift++ {
   112  		memoryInMiB >>= 1
   113  	}
   114  	if memoryInMiB == 15 {
   115  		memoryInMiB++
   116  		memoryInMiB <<= numShift
   117  	} else {
   118  		memoryInMiB = h.memoryInMiB
   119  	}
   120  	memoryShift, memoryMultiplier := format.GetMiltiplier(memoryInMiB << 20)
   121  	volumeShift, volumeMultiplier := format.GetMiltiplier(
   122  		h.totalVolumeBytes)
   123  	numVMs := h.getNumVMs()
   124  	tw.WriteRow("", "",
   125  		fmt.Sprintf("<a href=\"showHypervisor?%s\">%s</a>",
   126  			machine.Hostname, machine.Hostname),
   127  		fmt.Sprintf("<a href=\"http://%s:%d/\">%s</a>",
   128  			machine.Hostname, constants.HypervisorPortNumber,
   129  			h.getHealthStatus()),
   130  		machine.HostIpAddress.String(),
   131  		h.serialNumber,
   132  		h.location,
   133  		machineType,
   134  		fmt.Sprintf("%s/%d",
   135  			format.FormatMilli(h.allocatedMilliCPUs), h.numCPUs),
   136  		fmt.Sprintf("%d/%d %sB",
   137  			h.allocatedMemory<<20>>memoryShift,
   138  			memoryInMiB<<20>>memoryShift,
   139  			memoryMultiplier),
   140  		fmt.Sprintf("%d/%d %sB",
   141  			h.allocatedVolumeBytes>>volumeShift,
   142  			h.totalVolumeBytes>>volumeShift,
   143  			volumeMultiplier),
   144  		fmt.Sprintf("<a href=\"http://%s:%d/listVMs\">%d</a>",
   145  			machine.Hostname, constants.HypervisorPortNumber,
   146  			numVMs),
   147  	)
   148  	return numVMs
   149  }
   150  
   151  func (m *Manager) listHypervisors(topologyDir string, showFilter int,
   152  	subnetId string,
   153  	tagsToMatch *tagmatcher.TagMatcher) (hypervisorList, error) {
   154  	m.mutex.RLock()
   155  	defer m.mutex.RUnlock()
   156  	machines, err := m.topology.ListMachines(topologyDir)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	hypervisors := make([]*hypervisorType, 0, len(machines))
   161  	for _, machine := range machines {
   162  		if subnetId != "" {
   163  			hasSubnet, _ := m.topology.CheckIfMachineHasSubnet(
   164  				machine.Hostname, subnetId)
   165  			if !hasSubnet {
   166  				continue
   167  			}
   168  		}
   169  		hypervisor := m.hypervisors[machine.Hostname]
   170  		if tagsToMatch != nil {
   171  			if !tagsToMatch.MatchEach(machine.Tags) &&
   172  				!tagsToMatch.MatchEach(hypervisor.localTags) {
   173  				continue
   174  			}
   175  		}
   176  		switch showFilter {
   177  		case showOK:
   178  			if hypervisor.probeStatus == probeStatusConnected &&
   179  				(hypervisor.healthStatus == "" ||
   180  					hypervisor.healthStatus == "healthy") {
   181  				hypervisors = append(hypervisors, hypervisor)
   182  			}
   183  		case showConnected:
   184  			if hypervisor.probeStatus == probeStatusConnected {
   185  				hypervisors = append(hypervisors, hypervisor)
   186  			}
   187  		case showDisabled:
   188  			if hypervisor.probeStatus == probeStatusConnected &&
   189  				hypervisor.disabled {
   190  				hypervisors = append(hypervisors, hypervisor)
   191  			}
   192  		case showAll:
   193  			hypervisors = append(hypervisors, hypervisor)
   194  		case showOff:
   195  			if hypervisor.probeStatus == probeStatusOff {
   196  				hypervisors = append(hypervisors, hypervisor)
   197  			}
   198  		}
   199  	}
   200  	return hypervisors, nil
   201  }
   202  
   203  func (m *Manager) listHypervisorsHandler(w http.ResponseWriter,
   204  	req *http.Request) {
   205  	writer := bufio.NewWriter(w)
   206  	defer writer.Flush()
   207  	_, err := m.getTopology()
   208  	if err != nil {
   209  		fmt.Fprintln(writer, err)
   210  		return
   211  	}
   212  	parsedQuery := url.ParseQuery(req.URL)
   213  	showFilter := showAll
   214  	switch parsedQuery.Table["state"] {
   215  	case "connected":
   216  		showFilter = showConnected
   217  	case "disabled":
   218  		showFilter = showDisabled
   219  	case "OK":
   220  		showFilter = showOK
   221  	case "off":
   222  		showFilter = showOff
   223  	}
   224  	locationFilter := parsedQuery.Table["location"]
   225  	hypervisors, err := m.listHypervisors(locationFilter, showFilter, "", nil)
   226  	if err != nil {
   227  		fmt.Fprintln(writer, err)
   228  		return
   229  	}
   230  	sort.Sort(hypervisors)
   231  	if parsedQuery.OutputType() == url.OutputTypeText {
   232  		for _, hypervisor := range hypervisors {
   233  			fmt.Fprintln(writer, hypervisor.machine.Hostname)
   234  		}
   235  		return
   236  	}
   237  	if parsedQuery.OutputType() == url.OutputTypeJson {
   238  		json.WriteWithIndent(writer, "    ", hypervisors)
   239  		return
   240  	}
   241  	fmt.Fprintf(writer, "<title>List of hypervisors</title>\n")
   242  	writer.WriteString(commonStyleSheet)
   243  	fmt.Fprintln(writer, "<body>")
   244  	fmt.Fprintln(writer, `<table border="1" style="width:100%">`)
   245  	tw, _ := html.NewTableWriter(writer, true,
   246  		"Name", "Status", "IP Addr", "Serial Number", "Location", "Type",
   247  		"CPUs", "RAM", "Storage", "NumVMs")
   248  	var numVMs uint
   249  	for _, hypervisor := range hypervisors {
   250  		numVMs += hypervisor.writeStats(tw)
   251  	}
   252  	writeHypervisorTotalsStats(hypervisors, locationFilter, numVMs, tw)
   253  	tw.Close()
   254  	fmt.Fprintln(writer, "</body>")
   255  }
   256  
   257  func (m *Manager) listHypervisorsInLocation(
   258  	request proto.ListHypervisorsInLocationRequest) (
   259  	proto.ListHypervisorsInLocationResponse, error) {
   260  	showFilter := showOK
   261  	if request.IncludeUnhealthy {
   262  		showFilter = showConnected
   263  	}
   264  	hypervisors, err := m.listHypervisors(request.Location, showFilter,
   265  		request.SubnetId, tagmatcher.New(request.HypervisorTagsToMatch, false))
   266  	if err != nil {
   267  		return proto.ListHypervisorsInLocationResponse{}, err
   268  	}
   269  	addresses := make([]string, 0, len(hypervisors))
   270  	var tagsForHypervisors []tags.Tags
   271  	for _, hypervisor := range hypervisors {
   272  		addresses = append(addresses,
   273  			fmt.Sprintf("%s:%d",
   274  				hypervisor.machine.Hostname, constants.HypervisorPortNumber))
   275  		if len(request.TagsToInclude) > 0 {
   276  			hypervisorTags := make(tags.Tags)
   277  			for _, key := range request.TagsToInclude {
   278  				if value, ok := hypervisor.machine.Tags[key]; ok {
   279  					hypervisorTags[key] = value
   280  				}
   281  				if value, ok := hypervisor.localTags[key]; ok {
   282  					hypervisorTags[key] = value
   283  				}
   284  			}
   285  			tagsForHypervisors = append(tagsForHypervisors, hypervisorTags)
   286  		}
   287  	}
   288  	return proto.ListHypervisorsInLocationResponse{
   289  		HypervisorAddresses: addresses,
   290  		TagsForHypervisors:  tagsForHypervisors,
   291  	}, nil
   292  }
   293  
   294  func (list hypervisorList) Len() int {
   295  	return len(list)
   296  }
   297  
   298  func (list hypervisorList) Less(i, j int) bool {
   299  	if list[i].location < list[j].location {
   300  		return true
   301  	} else if list[i].location > list[j].location {
   302  		return false
   303  	} else {
   304  		return list[i].machine.Hostname < list[j].machine.Hostname
   305  	}
   306  }
   307  
   308  func (list hypervisorList) Swap(i, j int) {
   309  	list[i], list[j] = list[j], list[i]
   310  }