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 }