github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/fleetmanager/hypervisors/listVMs.go (about) 1 package hypervisors 2 3 import ( 4 "bufio" 5 "fmt" 6 "net" 7 "net/http" 8 "sort" 9 10 "github.com/Cloud-Foundations/Dominator/lib/constants" 11 "github.com/Cloud-Foundations/Dominator/lib/format" 12 "github.com/Cloud-Foundations/Dominator/lib/html" 13 "github.com/Cloud-Foundations/Dominator/lib/json" 14 "github.com/Cloud-Foundations/Dominator/lib/url" 15 "github.com/Cloud-Foundations/Dominator/lib/verstr" 16 proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 17 ) 18 19 const commonStyleSheet string = `<style> 20 table, th, td { 21 border-collapse: collapse; 22 } 23 </style> 24 ` 25 26 func getVmListFromMap(vmMap map[string]*vmInfoType, doSort bool) []*vmInfoType { 27 vms := make([]*vmInfoType, 0, len(vmMap)) 28 if doSort { 29 ipAddrs := make([]string, 0, len(vmMap)) 30 for ipAddr := range vmMap { 31 ipAddrs = append(ipAddrs, ipAddr) 32 } 33 verstr.Sort(ipAddrs) 34 for _, ipAddr := range ipAddrs { 35 vms = append(vms, vmMap[ipAddr]) 36 } 37 } else { 38 for _, vm := range vmMap { 39 vms = append(vms, vm) 40 } 41 } 42 return vms 43 } 44 45 func (m *Manager) listVMs(writer *bufio.Writer, vms []*vmInfoType, 46 primaryOwnerFilter string, outputType uint) (map[string]struct{}, error) { 47 topology, err := m.getTopology() 48 if err != nil { 49 return nil, err 50 } 51 var tw *html.TableWriter 52 if outputType == url.OutputTypeHtml { 53 fmt.Fprintf(writer, "<title>List of VMs</title>\n") 54 writer.WriteString(commonStyleSheet) 55 fmt.Fprintln(writer, `<table border="1" style="width:100%">`) 56 tw, _ = html.NewTableWriter(writer, true, "IP Addr", "Name(tag)", 57 "State", "RAM", "CPU", "Num Volumes", "Storage", "Primary Owner", 58 "Hypervisor", "Location") 59 } 60 primaryOwnersMap := make(map[string]struct{}) 61 for _, vm := range vms { 62 if primaryOwnerFilter != "" { 63 if vm.OwnerUsers[0] != primaryOwnerFilter { 64 primaryOwnersMap[vm.OwnerUsers[0]] = struct{}{} 65 continue 66 } 67 } else { 68 primaryOwnersMap[vm.OwnerUsers[0]] = struct{}{} 69 } 70 switch outputType { 71 case url.OutputTypeText: 72 fmt.Fprintln(writer, vm.ipAddr) 73 case url.OutputTypeHtml: 74 var background, foreground string 75 if vm.hypervisor.probeStatus == probeStatusOff { 76 foreground = "#ff8080" 77 } else if vm.hypervisor.probeStatus != probeStatusConnected { 78 foreground = "red" 79 } else if vm.hypervisor.healthStatus == "at risk" { 80 foreground = "#c00000" 81 } else if vm.hypervisor.healthStatus == "marginal" { 82 foreground = "#800000" 83 } 84 if vm.Uncommitted { 85 background = "yellow" 86 } else if topology.CheckIfIpIsReserved(vm.ipAddr) { 87 background = "orange" 88 } 89 tw.WriteRow(foreground, background, 90 fmt.Sprintf("<a href=\"http://%s:%d/showVM?%s\">%s</a>", 91 vm.hypervisor.machine.Hostname, 92 constants.HypervisorPortNumber, vm.ipAddr, vm.ipAddr), 93 vm.Tags["Name"], 94 vm.State.String(), 95 format.FormatBytes(vm.MemoryInMiB<<20), 96 fmt.Sprintf("%g", float64(vm.MilliCPUs)*1e-3), 97 vm.numVolumesTableEntry(), 98 vm.storageTotalTableEntry(), 99 vm.OwnerUsers[0], 100 fmt.Sprintf("<a href=\"http://%s:%d/\">%s</a>", 101 vm.hypervisor.machine.Hostname, 102 constants.HypervisorPortNumber, 103 vm.hypervisor.machine.Hostname), 104 vm.hypervisor.location, 105 ) 106 } 107 } 108 switch outputType { 109 case url.OutputTypeHtml: 110 fmt.Fprintln(writer, "</table>") 111 } 112 return primaryOwnersMap, nil 113 } 114 115 func (m *Manager) getVMs(doSort bool) []*vmInfoType { 116 m.mutex.RLock() 117 defer m.mutex.RUnlock() 118 return getVmListFromMap(m.vms, doSort) 119 } 120 121 func (m *Manager) listVMsHandler(w http.ResponseWriter, 122 req *http.Request) { 123 writer := bufio.NewWriter(w) 124 defer writer.Flush() 125 parsedQuery := url.ParseQuery(req.URL) 126 vms := m.getVMs(true) 127 if parsedQuery.OutputType() == url.OutputTypeJson { 128 json.WriteWithIndent(writer, " ", vms) 129 } 130 primaryOwnerFilter := parsedQuery.Table["primaryOwner"] 131 primaryOwnersMap, err := m.listVMs(writer, vms, primaryOwnerFilter, 132 parsedQuery.OutputType()) 133 if err != nil { 134 fmt.Fprintln(writer, err) 135 return 136 } 137 switch parsedQuery.OutputType() { 138 case url.OutputTypeHtml: 139 fmt.Fprintln(writer, "</body>") 140 primaryOwners := make([]string, 0, len(primaryOwnersMap)) 141 for primaryOwner := range primaryOwnersMap { 142 primaryOwners = append(primaryOwners, primaryOwner) 143 } 144 sort.Strings(primaryOwners) 145 fmt.Fprintln(writer, "Filter by primary owner:<br>") 146 for _, primaryOwner := range primaryOwners { 147 fmt.Fprintf(writer, 148 "<a href=\"listVMs?primaryOwner=%s\">%s</a><br>\n", 149 primaryOwner, primaryOwner) 150 } 151 } 152 } 153 154 func (m *Manager) listVMsInLocation(dirname string) ([]net.IP, error) { 155 hypervisors, err := m.listHypervisors(dirname, showAll, "") 156 if err != nil { 157 return nil, err 158 } 159 addresses := make([]net.IP, 0) 160 for _, hypervisor := range hypervisors { 161 hypervisor.mutex.RLock() 162 for _, vm := range hypervisor.vms { 163 addresses = append(addresses, vm.Address.IpAddress) 164 } 165 hypervisor.mutex.RUnlock() 166 } 167 return addresses, nil 168 } 169 170 func (vm *vmInfoType) numVolumesTableEntry() string { 171 var comment string 172 for _, volume := range vm.Volumes { 173 if comment == "" && volume.Format != proto.VolumeFormatRaw { 174 comment = `<font style="color:grey;font-size:12px"> (!RAW)</font>` 175 } 176 } 177 return fmt.Sprintf("%d%s", len(vm.Volumes), comment) 178 } 179 180 func (vm *vmInfoType) storageTotalTableEntry() string { 181 var storage uint64 182 for _, volume := range vm.Volumes { 183 storage += volume.Size 184 } 185 return format.FormatBytes(storage) 186 }