package pkg import ( "fmt" "regexp" "strconv" "strings" "github.rackspace.com/gorax/clients/sshAPI" ) // VolumeGroup : Holds information parsed from vgdisplay command type VolumeGroup struct { ActPV int `json:"act_pv"` AllocPE string `json:"alloc_pe"` AllocSize string `json:"alloc_size"` CurLV int `json:"cur_lv"` CurPV int `json:"cur_pv"` Format string `json:"format"` FreePE string `json:"free_pe"` FreeSize string `json:"free_size"` MaxLV int `json:"max_lv"` MaxPV int `json:"max_pv"` MetadataAreas int `json:"metadata_areas"` MetadataSeqNo int `json:"metadata_sequence_no"` OpenLV int `json:"open_lv"` PESize string `json:"pe_size"` SystemID string `json:"system_id"` TotalPE string `json:"total_pe"` VGAccess string `json:"vg_access"` VGName string `json:"vg_name"` VGSize string `json:"vg_size"` VGStatus string `json:"vg_status"` VGUUID string `json:"vg_uuid"` } type VolumeGroups []VolumeGroup func cleanVGEntry(in []string, start int) string { return strings.TrimSpace(strings.Join(in[start:], " ")) } func parseVG(lines []string) (VolumeGroup, error) { var vg VolumeGroup for _, line := range lines { line = strings.TrimSpace(line) fields := strings.Fields(line) switch { case strings.HasPrefix(line, "VG Name"): vg.VGName = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "System ID"): vg.SystemID = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "Format"): vg.Format = cleanVGEntry(fields, 1) case strings.HasPrefix(line, "Metadata Areas"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.MetadataAreas = i case strings.HasPrefix(line, "Metadata Sequence No"): i, err := strconv.Atoi(fields[3]) if err != nil { return vg, err } vg.MetadataSeqNo = i case strings.HasPrefix(line, "VG Access"): vg.VGAccess = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "VG Access"): vg.VGAccess = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "VG Status"): vg.VGStatus = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "MAX LV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.MaxLV = i case strings.HasPrefix(line, "Cur LV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.CurLV = i case strings.HasPrefix(line, "Open LV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.OpenLV = i case strings.HasPrefix(line, "Max PV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.MaxPV = i case strings.HasPrefix(line, "Cur PV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.CurPV = i case strings.HasPrefix(line, "Act PV"): i, err := strconv.Atoi(fields[2]) if err != nil { return vg, err } vg.ActPV = i case strings.HasPrefix(line, "VG Size"): vg.VGSize = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "PE Size"): vg.PESize = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "Total PE"): vg.TotalPE = cleanVGEntry(fields, 2) case strings.HasPrefix(line, "Alloc PE / Size"): if found := regexp.MustCompile(`Alloc\s+PE / Size\s+(.*) / (.*)`).FindStringSubmatch(line); len(found) == 3 { vg.AllocPE = found[1] vg.AllocSize = found[2] } case strings.HasPrefix(line, "Free PE / Size"): if found := regexp.MustCompile(`Free\s+PE / Size\s+(.*) / (.*)`).FindStringSubmatch(line); len(found) == 3 { vg.FreePE = found[1] vg.FreeSize = found[2] } case strings.HasPrefix(line, "VG UUID"): vg.VGUUID = cleanVGEntry(fields, 2) } } return vg, nil } func parseVolumeGroups(raw string) ([]VolumeGroup, error) { log := log.WithField("function", "parseVolumeGroups") log.Debug("Begin") var vgs []VolumeGroup var raws [][]string // Initialize with the first one raws = append(raws, []string{}) split := strings.Split(raw, "\n") active := -1 // first split the groups for _, line := range split { if strings.TrimSpace(line) == "--- Volume group ---" { log.Debug("New volume group found") // Add a new group to the raws if active > -1 { raws = append(raws, []string{}) } active++ } if active > -1 { log.WithField("active", active).Debugf("Appening line: %v", line) raws[active] = append(raws[active], line) } } // now parse the groups for _, g := range raws { parsed, err := parseVG(g) if err != nil { return nil, fmt.Errorf("error parsing volume group: %v", err) } vgs = append(vgs, parsed) } return vgs, nil } // VGDisplay : Executes vgdisplay command returns parsed results func (f5 *F5) VGDisplay() (VolumeGroups, error) { var cmds []*sshAPI.Command cmds = append(cmds, &sshAPI.Command{Command: "cli", Prompt: "# ", Timeout: 10}) cmds = append(cmds, &sshAPI.Command{Command: "bash", Prompt: "# ", Timeout: 10}) cmds = append(cmds, &sshAPI.Command{Command: "vgdisplay", Prompt: "# ", Timeout: 30}) resp, err := f5.sshClient.PostCommands(cmds) if err != nil { return nil, fmt.Errorf("error running vgdisplay command: %v", err) } var resString string for _, cmd := range resp.Commands { resString = resString + *cmd.Response } vgs, err := parseVolumeGroups(resString) if err != nil { return nil, fmt.Errorf("error parsing results of vgdisplay command: %v", err) } return vgs, nil }