blob: 04e18f7a7c56caa7092b742139698030e3b504b0 [file]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package utils
import (
"bufio"
"context"
"fmt"
"io/ioutil"
"os"
"sort"
"strconv"
"strings"
"github.com/golang/protobuf/proto"
"go.chromium.org/luci/common/errors"
"google.golang.org/protobuf/encoding/protojson"
fleet "infra/unifiedfleet/api/v1/models"
chromeosLab "infra/unifiedfleet/api/v1/models/chromeos/lab"
UfleetAPI "infra/unifiedfleet/api/v1/rpc"
UfleetUtil "infra/unifiedfleet/app/util"
)
// Interactive mode messages for user input
const (
InputDetails string = "Please enter the details: "
RequiredField string = "is a required field. It cannot be blank/empty."
WrongInput string = "\n WRONG INPUT!!\n"
ChooseOption string = "\n Choose an option\n"
ChooseChromePlatform string = "\n Choose a ChromePlatform\n"
ChooseMachineLSEPrototype string = "\n Choose a MachineLSE Prototype\n"
ChooseChameleonType string = "\n Choose a ChameleonType \n"
ChooseCameraType string = "\n Choose a CameraType \n"
ChooseAntennaConnection string = "\n Choose an AntennaConnection \n"
ChooseRouter string = "\n Choose a Router \n"
ChooseCableType string = "\n Choose a Cable \n"
ChooseCameraboxFacing string = "\n Choose a Facing for CameraBox \n"
ChoosePheripheralType string = "\n Choose a PheripheralType \n"
ChooseVirtualType string = "\n Choose a VirtualType \n"
OptionToEnter string = "\nDo you want to enter a "
OptionToEnterMore string = "\nDo you want to enter one more "
ChooseLab string = "\n Choose a Lab\n"
ChooseZone string = "\n Choose a Zone\n"
BroswerOrOSLab string = "1=\"Browser Lab\"\n2=\"OS Lab\"\n"
BrowserOrATLOrACSLab string = "1=\"Browser Lab\"\n2=\"ATL Lab\"\n3=\"ACS Lab\"\n"
DutOrLabstationOrServer string = "1=\"DUT\"\n2=\"Labstation\"\n3=\"Server\"\n"
DoesNotExist string = " doesnt not exist in the system. Please check and enter again."
AlreadyExists string = " already exists in the system. Please check and enter again."
ATL string = "ATL"
ACS string = "ACS"
Browser string = "Browser"
Unknown string = "Unknown"
maxPageSize int32 = 1000
YesNo string = " (y/n)"
ATLLab string = "atl:"
ACSLab string = "acs:"
BrowserLab string = "browser:"
MinMaxError string = "Maximum value must be greater than or equal to Minimum value."
)
// Input deatils for the input variable
//
// Key - input variable name
// Desc - description of the variable
// Required - if the variable is a required field
type Input struct {
Key string
Desc string
Required bool
}
// GetInteractiveInput collects the scanned string list.
func GetInteractiveInput() []string {
inputs := make([]string, 0)
fmt.Print("Please scan: ")
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
iput := scanner.Text()
if iput == "" {
break
}
inputs = append(inputs, iput)
fmt.Print("Continue (please enter without scanning if you finish): ")
}
return inputs
}
// GetSwitchInteractiveInput get switch input in interactive mode
//
// Name(string) -> Rack name(string) -> CapacityPort(int)
func GetSwitchInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, s *fleet.Switch) {
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if SwitchExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
}
s.Name = value
input = &Input{
Key: "Rack name",
Desc: "Name of the rack to associate this switch.",
Required: true,
}
case "Rack name":
if value != "" && !RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
s.Rack = value
input = &Input{
Key: "CapacityPort",
}
case "CapacityPort":
if value != "" {
port := getIntInput(value, input)
if port == -1 {
input.Desc = "Invalid number input. Please enter a valid input."
break
}
s.CapacityPort = port
}
input = nil
}
break
}
}
return
}
// GetMachineInteractiveInput get Machine input in interactive mode
//
// Name(string) -> Lab(enum) -> Browser/OS LAB(choice to branch) ->
// -> getBrowserMachine()/getOSMachine() -> Realm(string)
func GetMachineInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, machine *fleet.Machine, update bool) {
machine.Location = &fleet.Location{}
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
input.Desc = UfleetAPI.ValidName
break
}
if !update && MachineExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !MachineExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
machine.Name = value
input = &Input{
Key: "Lab",
Desc: fmt.Sprintf("%s%s", ChooseLab, createKeyValuePairs(fleet.Lab_name)),
}
case "Lab":
// TODO(eshwarn): revisit this logic with zone instead of lab
/*if value == "" || value == "0" {
input = &Input{
Key: "Browser/OS LAB",
Desc: fmt.Sprintf("%s%s", ChooseLab, BroswerOrOSLab),
Required: true,
}
} else {
option := getSelectionInput(value, fleet.Lab_name, input)
if option == -1 {
break
}
machine.Location.Lab = fleet.Lab(option)
input = &Input{
Key: "Realm",
}
if getLab(machine.Location.Lab) == Browser {
// Chorome Browser lab
getBrowserMachine(ctx, ic, scanner, machine)
} else if getLab(machine.Location.Lab) == ACS ||
getLab(machine.Location.Lab) == ATL {
// ChromeOS lab
getOSMachine(ctx, ic, scanner, machine)
} else {
// Unknown or fleet.Lab_LAB_CHROMEOS_SANTIEM
input = &Input{
Key: "Browser/OS LAB",
Desc: fmt.Sprintf("%s%s", ChooseLab, BroswerOrOSLab),
Required: true,
}
}
}*/
case "Browser/OS LAB":
if value == "1" {
// Chrome Browser lab
getBrowserMachine(ctx, ic, scanner, machine)
input = &Input{
Key: "Realm",
}
} else if value == "2" {
// Chrome OS lab
getOSMachine(ctx, ic, scanner, machine)
input = &Input{
Key: "Realm",
}
} else {
input.Desc = fmt.Sprintf("%s%s%s", WrongInput, ChooseLab, BroswerOrOSLab)
}
case "Realm":
machine.Realm = value
input = nil
}
break
}
}
}
// getOSMachine get Chrome OS Machine input in interactive mode
//
// Rack(string) -> Aisle(string) -> Row(string) -> Rack Number(string) ->
// -> Shelf(string) -> Position(string)
func getOSMachine(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machine *fleet.Machine) {
machine.Device = &fleet.Machine_ChromeosMachine{
ChromeosMachine: &fleet.ChromeOSMachine{},
}
input := &Input{
Key: "Rack",
Required: false,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Rack":
if value != "" && !RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
machine.Location.Rack = value
input = &Input{
Key: "Aisle",
}
case "Aisle":
machine.Location.Aisle = value
input = &Input{
Key: "Row",
}
case "Row":
machine.Location.Row = value
input = &Input{
Key: "Rack Number",
}
case "Rack Number":
machine.Location.RackNumber = value
input = &Input{
Key: "Shelf",
}
case "Shelf":
machine.Location.Shelf = value
input = &Input{
Key: "Position",
}
case "Position":
machine.Location.Position = value
input = nil
}
break
}
}
}
// getBrowserMachine get Browser Machine input in interactive mode
//
// Rack(string, resource) -> DisplayName(string) ->
// -> ChromePlatform(string, resource) -> KVM(string, resource) ->
// -> KVM Port(int) -> RPM(string, resource) -> RPM Port(int) ->
// -> Switch(string, resource) -> Switch Port(int) ->
// -> DeploymentTicket(string) -> Description(string)
func getBrowserMachine(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machine *fleet.Machine) {
browserMachine := &fleet.ChromeBrowserMachine{}
machine.Device = &fleet.Machine_ChromeBrowserMachine{
ChromeBrowserMachine: browserMachine,
}
chromePlatforms := getAllChromePlatforms(ctx, ic)
input := &Input{
Key: "Rack",
Required: false,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Rack":
if value != "" && !RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
} else {
machine.Location.Rack = value
input = &Input{
Key: "DisplayName",
}
}
case "DisplayName":
browserMachine.DisplayName = value
input = &Input{
Key: "ChromePlatform",
Desc: fmt.Sprintf("%s%s", ChooseChromePlatform, createKeyValuePairs(chromePlatforms)),
}
case "ChromePlatform":
if value != "" {
option := getSelectionInput(value, chromePlatforms, input)
if option == -1 {
break
}
browserMachine.ChromePlatform = chromePlatforms[option]
}
input = &Input{
Key: "KVM",
}
case "KVM":
if value != "" && !KVMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
browserMachine.KvmInterface = &fleet.KVMInterface{
Kvm: value,
}
if value != "" {
input = &Input{
Key: "KVM Port",
}
} else {
input = &Input{
Key: "RPM",
}
}
case "KVM Port":
if value != "" {
browserMachine.KvmInterface.PortName = value
}
input = &Input{
Key: "RPM",
}
case "RPM":
if value != "" && !RPMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
browserMachine.RpmInterface = &fleet.RPMInterface{
Rpm: value,
}
if value != "" {
input = &Input{
Key: "RPM Port",
}
} else {
input = &Input{
Key: "DeploymentTicket",
}
}
case "RPM Port":
if value != "" {
browserMachine.RpmInterface.PortName = value
}
input = &Input{
Key: "DeploymentTicket",
}
case "DeploymentTicket":
browserMachine.DeploymentTicket = value
input = &Input{
Key: "Description",
}
case "Description":
browserMachine.Description = value
input = nil
}
break
}
}
}
// GetMachinelseInteractiveInput get MachineLSE input in interactive mode
//
// Name(string) -> Broswer/ATL/ACS LAB(choice to branch) ->
// -> getBrowserMachineLse()/getOSMachineLse() ->
// -> Machine(repeated string, resource)
func GetMachinelseInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, machinelse *fleet.MachineLSE, update bool) {
input := &Input{
Key: "Hostname",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Hostname":
if !UfleetAPI.IDRegex.MatchString(value) {
input.Desc = UfleetAPI.ValidName
break
}
if !update && MachineLSEExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !MachineLSEExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
// Name and Hostname of a MachineLSE must be same.
machinelse.Name = value
machinelse.Hostname = value
input = &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s", ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
case "Broswer/ATL/ACS LAB":
input = &Input{
Key: "Machines (y/n)",
Desc: fmt.Sprintf("%sMachine?", OptionToEnter),
Required: true,
}
switch value {
case "1":
// Browser lab
getPrototype(ctx, ic, scanner, machinelse, BrowserLab)
getBrowserMachineLse(ctx, ic, scanner, machinelse)
case "2":
// ATL lab
getPrototype(ctx, ic, scanner, machinelse, ATLLab)
getOSMachineLse(ctx, ic, scanner, machinelse, false)
case "3":
// ACS lab
getPrototype(ctx, ic, scanner, machinelse, ACSLab)
getOSMachineLse(ctx, ic, scanner, machinelse, true)
default:
input = &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s%s", WrongInput, ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
}
// repeated Machines
case "Machines (y/n)":
vals, done := getRepeatedStringInput(ctx, ic, scanner, value, "Machine", input, true)
if done {
machinelse.Machines = vals
input = nil
}
}
break
}
}
}
// getPrototype get MachineLSE prototype
//
// MachineLSEPrototype(selection) ->
func getPrototype(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE, lab string) {
machineLSEPrototypes := getAllMachineLSEPrototypes(ctx, ic, lab)
if len(machineLSEPrototypes) == 0 {
return
}
input := &Input{
Key: "MachineLSEPrototype",
Desc: fmt.Sprintf("%s%s", ChooseMachineLSEPrototype, createKeyValuePairs(machineLSEPrototypes)),
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "MachineLSEPrototype":
if value != "" {
option := getSelectionInput(value, machineLSEPrototypes, input)
if option == -1 {
break
}
machinelse.MachineLsePrototype = machineLSEPrototypes[option]
}
input = nil
}
break
}
}
}
// getOSMachineLse get ChormeOS MachineLSE input in interactive mode
//
// DUT, Labstation or Server(choice to branch) ->
// -> getOSDeviceLse()/getOSServerLse()
func getOSMachineLse(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE, acs bool) {
machinelse.Lse = &fleet.MachineLSE_ChromeosMachineLse{
ChromeosMachineLse: &fleet.ChromeOSMachineLSE{},
}
input := &Input{
Key: "DUT, Labstation or Server",
Desc: fmt.Sprintf("%s%s", ChooseOption, DutOrLabstationOrServer),
Required: true,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "DUT, Labstation or Server":
if value == "1" {
// ChromeOSDeviceLse - DUT
getOSDeviceLse(ctx, ic, scanner, machinelse, acs, true)
input = nil
} else if value == "2" {
// ChromeOSDeviceLse - Labstation
getOSDeviceLse(ctx, ic, scanner, machinelse, acs, false)
input = nil
} else if value == "3" {
// ChromeOSServerLse - Server
getOSServerLse(ctx, ic, scanner, machinelse)
input = nil
} else {
input.Desc = fmt.Sprintf("%s%s%s", WrongInput, ChooseOption, DutOrLabstationOrServer)
}
}
break
}
}
}
// getOSDeviceLse get ChromeOSDeviceLSE input in interactive mode
//
// RPM(string, resource) -> RPM Port(int) -> Switch(string, resource) ->
// -> Switch Port(int) -> geDut()/getLabstation()
func getOSDeviceLse(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE, acs, dut bool) {
deviceLse := &fleet.ChromeOSDeviceLSE{}
machinelse.GetChromeosMachineLse().ChromeosLse = &fleet.ChromeOSMachineLSE_DeviceLse{
DeviceLse: deviceLse,
}
input := &Input{
Key: "RPM",
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// ChromeOSDeviceLSE
// RPMInterface
case "RPM":
if value != "" && !RPMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
deviceLse.RpmInterface = &fleet.RPMInterface{
Rpm: value,
}
if value != "" {
input = &Input{
Key: "RPM Port",
}
} else {
input = &Input{
Key: "Switch",
}
}
case "RPM Port":
if value != "" {
deviceLse.GetRpmInterface().PortName = value
}
input = &Input{
Key: "Switch",
}
// SwitchInterface
case "Switch":
if value != "" && !SwitchExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
deviceLse.NetworkDeviceInterface = &fleet.SwitchInterface{
Switch: value,
}
if value != "" {
input = &Input{
Key: "Switch Port",
}
} else {
if dut {
// DUT
getDut(ctx, ic, scanner, machinelse, acs)
} else {
// Labstation
getLabstation(ctx, ic, scanner, machinelse)
}
input = nil
}
case "Switch Port":
if value != "" {
deviceLse.GetNetworkDeviceInterface().PortName = value
}
if dut {
// DUT
getDut(ctx, ic, scanner, machinelse, acs)
} else {
// Labstation
getLabstation(ctx, ic, scanner, machinelse)
}
input = nil
}
break
}
}
}
// getDut get DeviceUnderTest input in interactive mode
//
// Servo Hostname(string) -> Servo Port(int) -> Servo Serial(string) ->
// -> Servo Type(string) -> RPM PowerunitName(string) ->
// -> RPM PowerunitOutlet(string) -> Pools(repeated string) -> getACSLabConfig()
func getDut(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE, acs bool) {
dut := &chromeosLab.DeviceUnderTest{
Hostname: machinelse.Hostname,
Peripherals: &chromeosLab.Peripherals{
Servo: &chromeosLab.Servo{},
Rpm: &chromeosLab.OSRPM{},
},
}
machinelse.GetChromeosMachineLse().GetDeviceLse().Device = &fleet.ChromeOSDeviceLSE_Dut{
Dut: dut,
}
input := &Input{
Key: "Servo Hostname",
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// DeviceUnderTest Dut Config
// Peripherals
// Servo
case "Servo Hostname":
if value != "" && !MachineLSEExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("Specified labstation %s is not "+
"deployed. Please use deploy-machine command to "+
"deploy the labstation %s first.", value, value)
break
}
dut.GetPeripherals().GetServo().ServoHostname = value
if value != "" {
input = &Input{
Key: "Servo Port",
}
} else {
input = &Input{
Key: "RPM PowerunitName",
}
}
case "Servo Port":
if value != "" {
port := getIntInput(value, input)
if port == -1 {
break
}
dut.GetPeripherals().GetServo().ServoPort = port
}
input = &Input{
Key: "Servo Serial",
}
case "Servo Serial":
// TODO(eshwarn) : this is available in Hart indexed by asset tag
dut.GetPeripherals().GetServo().ServoSerial = value
input = &Input{
Key: "Servo Type",
}
case "Servo Type":
// TODO(eshwarn) : this is available in Hart as google code name
dut.GetPeripherals().GetServo().ServoType = value
input = &Input{
Key: "RPM PowerunitName",
}
case "RPM PowerunitName":
dut.GetPeripherals().GetRpm().PowerunitName = value
input = &Input{
Key: "RPM PowerunitOutlet",
}
case "RPM PowerunitOutlet":
dut.GetPeripherals().GetRpm().PowerunitOutlet = value
input = &Input{
Key: "Pools (y/n)",
Desc: fmt.Sprintf("%sPool?", OptionToEnter),
Required: true,
}
// repeated pools
case "Pools (y/n)":
vals, done := getRepeatedStringInput(nil, nil, scanner, value, "Pool", input, false)
if done {
dut.Pools = vals
if acs {
getACSLabConfig(scanner, machinelse)
}
input = nil
}
}
break
}
}
}
// getACSLabConfig get ACSLab Config input in interactive mode
//
// Chameleon Peripherals(repeated enum) -> Audio Board(bool) ->
// -> Cameras(repeated enum) -> Audio Box(bool) -> Atrus(bool) ->
// -> Wificell(bool) -> AntennaConnection(enum) -> Router(enum) ->
// -> Touch Mimo(bool) -> Carrier(string) -> Camerabox(bool) ->
// -> Chaos(bool) -> Cable(repeated enum) -> Camerabox Info(enum)
func getACSLabConfig(scanner *bufio.Scanner, machinelse *fleet.MachineLSE) {
peripherals := machinelse.GetChromeosMachineLse().GetDeviceLse().GetDut().GetPeripherals()
peripherals.Chameleon = &chromeosLab.Chameleon{}
input := &Input{
Key: "Chameleon Peripherals (y/n)",
Desc: fmt.Sprintf("%sChameleon Peripheral?", OptionToEnter),
Required: true,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// repeated enum Chameleon Peripherals
case "Chameleon Peripherals (y/n)":
vals, done := getRepeatedEnumInput(scanner, value, "Chameleon Peripheral", chromeosLab.ChameleonType_name, input)
if done {
cps := make([]chromeosLab.ChameleonType, 0, len(vals))
for _, val := range vals {
cps = append(cps, chromeosLab.ChameleonType(val))
}
peripherals.GetChameleon().ChameleonPeripherals = cps
input = &Input{
Key: "Audio Board (y/n)",
Required: true,
}
}
// bool Audio Board
case "Audio Board (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.GetChameleon().AudioBoard = flag
input = &Input{
Key: "Cameras (y/n)",
Desc: fmt.Sprintf("%sCamera?", OptionToEnter),
Required: true,
}
}
// repeated enum Camera
case "Cameras (y/n)":
vals, done := getRepeatedEnumInput(scanner, value, "Camera", chromeosLab.CameraType_name, input)
if done {
cameras := make([]*chromeosLab.Camera, 0, len(vals))
for _, val := range vals {
camera := &chromeosLab.Camera{
CameraType: chromeosLab.CameraType(val),
}
cameras = append(cameras, camera)
}
peripherals.ConnectedCamera = cameras
input = &Input{
Key: "Audio Box (y/n)",
Required: true,
}
}
// bool Audio Box
case "Audio Box (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.Audio = &chromeosLab.Audio{
AudioBox: flag,
}
input = &Input{
Key: "Atrus (y/n)",
Required: true,
}
}
// bool Artus
case "Atrus (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.GetAudio().Atrus = flag
input = &Input{
Key: "Wificell (y/n)",
Required: true,
}
}
// bool Wificell
case "Wificell (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.Wifi = &chromeosLab.Wifi{
Wificell: flag,
}
input = &Input{
Key: "AntennaConnection",
Desc: fmt.Sprintf("%s%s", ChooseAntennaConnection, createKeyValuePairs(chromeosLab.Wifi_AntennaConnection_name)),
}
}
// enum AntennaConnection
case "AntennaConnection":
if value != "" {
option := getSelectionInput(value, chromeosLab.Wifi_AntennaConnection_name, input)
if option == -1 {
break
}
peripherals.GetWifi().AntennaConn = chromeosLab.Wifi_AntennaConnection(option)
}
input = &Input{
Key: "Router",
Desc: fmt.Sprintf("%s%s", ChooseRouter, createKeyValuePairs(chromeosLab.Wifi_Router_name)),
}
// enum Router
case "Router":
if value != "" {
option := getSelectionInput(value, chromeosLab.Wifi_Router_name, input)
if option == -1 {
break
}
peripherals.GetWifi().Router = chromeosLab.Wifi_Router(option)
}
input = &Input{
Key: "Touch Mimo (y/n)",
Required: true,
}
// bool Touch Mimo
case "Touch Mimo (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.Touch = &chromeosLab.Touch{
Mimo: flag,
}
input = &Input{
Key: "Carrier",
}
}
case "Carrier":
peripherals.Carrier = value
input = &Input{
Key: "Camerabox (y/n)",
}
// bool CameraBox
case "Camerabox (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.Camerabox = flag
input = &Input{
Key: "Chaos (y/n)",
}
}
// bool chaos
case "Chaos (y/n)":
flag, done := getBoolInput(value, input)
if done {
peripherals.Chaos = flag
input = &Input{
Key: "Cable (y/n)",
Desc: fmt.Sprintf("%sCable?", OptionToEnter),
Required: true,
}
}
// repeated enum Cable
case "Cable (y/n)":
vals, done := getRepeatedEnumInput(scanner, value, "Cable", chromeosLab.CableType_name, input)
if done {
cables := make([]*chromeosLab.Cable, 0, len(vals))
for _, val := range vals {
cable := &chromeosLab.Cable{
Type: chromeosLab.CableType(val),
}
cables = append(cables, cable)
}
peripherals.Cable = cables
input = &Input{
Key: "Camerabox Info",
Desc: fmt.Sprintf("%s%s", ChooseCameraboxFacing, createKeyValuePairs(chromeosLab.Camerabox_Facing_name)),
}
}
// enum CameraBox Info
case "Camerabox Info":
if value != "" {
option := getSelectionInput(value, chromeosLab.Camerabox_Facing_name, input)
if option == -1 {
break
}
peripherals.CameraboxInfo = &chromeosLab.Camerabox{
Facing: chromeosLab.Camerabox_Facing(option),
}
}
input = nil
}
break
}
}
}
// getLabstation get Labstation input in interactive mode
//
// -> RPM PowerunitName(string) -> RPM PowerunitOutlet(string) ->
// -> Pools(repeated string) -> getServos()
func getLabstation(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE) {
labstation := &chromeosLab.Labstation{
// Hostname for a Labstation must be the same as the MachineLSE name.
// MachineLSE Name is the MachineLSE Hostname
// we use Labstation Hostname(ServoHostname) from a DUT to update the
// Labstation with Servo info
Hostname: machinelse.GetHostname(),
Rpm: &chromeosLab.OSRPM{},
}
machinelse.GetChromeosMachineLse().GetDeviceLse().Device = &fleet.ChromeOSDeviceLSE_Labstation{
Labstation: labstation,
}
input := &Input{
Key: "RPM PowerunitName",
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// Labstation
case "RPM PowerunitName":
labstation.GetRpm().PowerunitName = value
input = &Input{
Key: "RPM PowerunitOutlet",
}
case "RPM PowerunitOutlet":
labstation.GetRpm().PowerunitOutlet = value
input = &Input{
Key: "Pools (y/n)",
Desc: fmt.Sprintf("%sPool?", OptionToEnter),
Required: true,
}
// repeated pools
case "Pools (y/n)":
vals, done := getRepeatedStringInput(nil, nil, scanner, value, "Pool", input, false)
if done {
labstation.Pools = vals
getServos(ctx, ic, machinelse)
input = nil
}
}
break
}
}
}
// getServos get the servos from existing MachineLSE
//
// MachineLSE Labstation update is not allowed to change the servo info in the
// Labstation, so during update call we get the existing servo info from the
// labstation and copy it to the input. For Create call we do nothing.
func getServos(ctx context.Context, ic UfleetAPI.FleetClient, machinelse *fleet.MachineLSE) {
labstationMachineLse, _ := ic.GetMachineLSE(ctx, &UfleetAPI.GetMachineLSERequest{
Name: UfleetUtil.AddPrefix(UfleetUtil.MachineLSECollection, machinelse.GetName()),
})
if labstationMachineLse != nil {
machinelse.GetChromeosMachineLse().GetDeviceLse().GetLabstation().Servos = labstationMachineLse.GetChromeosMachineLse().GetDeviceLse().GetLabstation().GetServos()
}
}
// getOSServerLse get ChromeOSServerLSE input in interactive mode
//
// Vlan(string, resource) -> Service Port(int)
func getOSServerLse(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE) {
serverLse := &fleet.ChromeOSServerLSE{}
machinelse.GetChromeosMachineLse().ChromeosLse = &fleet.ChromeOSMachineLSE_ServerLse{
ServerLse: serverLse,
}
input := &Input{
Key: "Vlan",
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// ChromeOSServerLSE
// Vlan
case "Vlan":
if value != "" && !VlanExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
serverLse.SupportedRestrictedVlan = value
input = &Input{
Key: "Service Port",
}
case "Service Port":
if value != "" {
port := getIntInput(value, input)
if port == -1 {
break
}
serverLse.ServicePort = port
}
input = nil
}
break
}
}
}
// getBrowserMachineLse get Browser MachineLSE input in interactive mode
//
// VM capacity(int) -> getVms()
func getBrowserMachineLse(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE) {
machinelse.Lse = &fleet.MachineLSE_ChromeBrowserMachineLse{
ChromeBrowserMachineLse: &fleet.ChromeBrowserMachineLSE{},
}
input := &Input{
Key: "VM Capactiy",
Required: true,
Desc: "The maximum number of the VMs allowed on this Browser Machine LSE.",
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "VM Capactiy":
if value != "" {
capacity := getIntInput(value, input)
if capacity == -1 {
break
}
machinelse.GetChromeBrowserMachineLse().VmCapacity = capacity
}
getVms(ctx, ic, scanner, machinelse)
input = nil
}
break
}
}
}
// getVms get Vms for Browser MachineLSE input in interactive mode
//
// -> VMs(repeated) -> VM Name(string) -> VM OS Version(string) ->
// -> VM OS Description(string) -> VM Mac Address(string) -> VM Hostname(string)
func getVms(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, machinelse *fleet.MachineLSE) {
maxVMAllowed := machinelse.GetChromeBrowserMachineLse().GetVmCapacity()
if maxVMAllowed == 0 {
fmt.Print("\nvm_capacity is 0. Increase vm_capacity to add VMs.\n")
return
}
input := &Input{
Key: "VMs (y/n)",
Desc: fmt.Sprintf("%sVM?", OptionToEnter),
Required: true,
}
vms := make([]*fleet.VM, 0, 0)
var vm *fleet.VM
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// ChromeBrowserMachineLSE
// repeated VMs
case "VMs (y/n)":
value = strings.ToLower(value)
if value == "y" {
input = &Input{
Key: "VM Name",
}
} else if value == "n" {
input = nil
} else {
input = &Input{
Key: "VMs (y/n)",
Desc: fmt.Sprintf("%s%sVM?", WrongInput, OptionToEnter),
Required: true,
}
}
case "VM Name":
vm = &fleet.VM{
Name: value,
}
input = &Input{
Key: "VM OS Version",
}
case "VM OS Version":
vm.OsVersion = &fleet.OSVersion{
Value: value,
}
input = &Input{
Key: "VM OS Description",
}
case "VM OS Description":
vm.GetOsVersion().Description = value
input = &Input{
Key: "VM Mac Address",
}
case "VM Mac Address":
vm.MacAddress = value
input = &Input{
Key: "VM Hostname",
}
case "VM Hostname":
vm.Hostname = value
vms = append(vms, vm)
machinelse.GetChromeBrowserMachineLse().Vms = vms
if len(vms) == int(maxVMAllowed) {
fmt.Print("\nYou have added the maximum VMs for this MachineLSE.\n" +
"If you want to add more please increase the vm_capacity.\n")
return
}
input = &Input{
Key: "VMs (y/n)",
Desc: fmt.Sprintf("%sVM?", OptionToEnterMore),
Required: true,
}
}
break
}
}
}
// GetMachinelsePrototypeInteractiveInput gets MachineLSEPrototype input in interactive mode
//
// Name(string) -> Broswer/ATL/ACS LAB(choice) -> Occupied Capacity(int) ->
// -> getPeripheralRequirements() -> getVirtualRequirements()
func GetMachinelsePrototypeInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, mlsep *fleet.MachineLSEPrototype, update bool) {
input := &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s", ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
var prefix string
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Broswer/ATL/ACS LAB":
input = &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
switch value {
case "1":
// Browser lab
prefix = BrowserLab
case "2":
// ATL lab
prefix = ATLLab
case "3":
// ACS lab
prefix = ACSLab
default:
input = &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s%s", WrongInput, ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
}
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
input.Desc = UfleetAPI.ValidName
break
}
if !update && MachineLSEPrototypeExists(ctx, ic, prefix+value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !MachineLSEPrototypeExists(ctx, ic, prefix+value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
mlsep.Name = prefix + value
input = &Input{
Key: "Occupied Capacity",
Desc: "Indicates the Rack Unit capacity of this setup, " +
"corresponding to a Rack’s Rack Unit capacity.",
}
case "Occupied Capacity":
if value != "" {
val := getIntInput(value, input)
if val == -1 {
break
}
mlsep.OccupiedCapacityRu = val
}
mlsep.PeripheralRequirements = getPeripheralRequirements(scanner)
getVirtualRequirements(scanner, mlsep)
input = nil
}
break
}
}
}
// GetRacklsePrototypeInteractiveInput gets RackLSEPrototype input in interactive mode
//
// Name(string) -> Broswer/ATL/ACS LAB(choice) -> getPeripheralRequirements()
func GetRacklsePrototypeInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, rlsep *fleet.RackLSEPrototype, update bool) {
input := &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s", ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
var prefix string
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Broswer/ATL/ACS LAB":
input = &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
switch value {
case "1":
// Browser lab
prefix = BrowserLab
case "2":
// ATL lab
prefix = ATLLab
case "3":
// ACS lab
prefix = ACSLab
default:
input = &Input{
Key: "Broswer/ATL/ACS LAB",
Desc: fmt.Sprintf("%s%s%s", WrongInput, ChooseLab, BrowserOrATLOrACSLab),
Required: true,
}
}
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
input.Desc = UfleetAPI.ValidName
break
}
if !update && RackLSEPrototypeExists(ctx, ic, prefix+value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !RackLSEPrototypeExists(ctx, ic, prefix+value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
rlsep.Name = prefix + value
rlsep.PeripheralRequirements = getPeripheralRequirements(scanner)
input = nil
}
break
}
}
}
// getPeripheralRequirements get PeripheralRequirements for
// Machine/Rack LSEPrototype input in interactive mode
//
// PeripheralRequirements(repeated) -> PeripheralType(enum) -> min(int) ->
// -> max(int)
func getPeripheralRequirements(scanner *bufio.Scanner) []*fleet.PeripheralRequirement {
input := &Input{
Key: "PeripheralRequirements (y/n)",
Desc: fmt.Sprintf("%sPeripheralRequirement?", OptionToEnter),
Required: true,
}
prs := make([]*fleet.PeripheralRequirement, 0, 0)
var pr *fleet.PeripheralRequirement
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return prs
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// repeated PeripheralRequirements
case "PeripheralRequirements (y/n)":
value = strings.ToLower(value)
if value == "y" {
input = &Input{
Key: "PeripheralType",
Desc: fmt.Sprintf("%s%s", ChoosePheripheralType, createKeyValuePairs(fleet.PeripheralType_name)),
}
} else if value == "n" {
return prs
} else {
input = &Input{
Key: "PeripheralRequirements (y/n)",
Desc: fmt.Sprintf("%s%sPeripheralRequirement?", WrongInput, OptionToEnter),
Required: true,
}
}
case "PeripheralType":
if value == "" || value == "0" {
pr = &fleet.PeripheralRequirement{}
} else {
option := getSelectionInput(value, fleet.PeripheralType_name, input)
if option == -1 {
break
}
pr = &fleet.PeripheralRequirement{
PeripheralType: fleet.PeripheralType(option),
}
}
input = &Input{
Key: "Minimum Pheripherals",
Desc: "The minimum/maximum number of the peripherals " +
"that is needed by a LSE, e.g. A test needs 1-3 bluetooth " +
"bt peers to be set up.",
}
case "Minimum Pheripherals":
if value != "" {
val := getIntInput(value, input)
if val == -1 {
break
}
pr.Min = val
}
input = &Input{
Key: "Maximum Pheripherals",
}
case "Maximum Pheripherals":
if value != "" {
val := getIntInput(value, input)
if val == -1 {
break
}
if val < pr.Min {
input.Desc = fmt.Sprintf("%s%s", WrongInput, MinMaxError)
break
}
pr.Max = val
}
prs = append(prs, pr)
input = &Input{
Key: "PeripheralRequirements (y/n)",
Desc: fmt.Sprintf("%sPeripheralRequirement?", OptionToEnterMore),
Required: true,
}
}
break
}
}
return nil
}
// getVirtualRequirements get VirtualRequirements for MachineLSEPrototype input in interactive mode
//
// VirtualRequirements(repeated) -> VirtualType(enum) -> min(int) -> max(int)
func getVirtualRequirements(scanner *bufio.Scanner, mlsep *fleet.MachineLSEPrototype) {
input := &Input{
Key: "VirtualRequirements (y/n)",
Desc: fmt.Sprintf("%sVirtualRequirement?", OptionToEnter),
Required: true,
}
prs := make([]*fleet.VirtualRequirement, 0, 0)
var pr *fleet.VirtualRequirement
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
// repeated VirtualRequirements
case "VirtualRequirements (y/n)":
value = strings.ToLower(value)
if value == "y" {
input = &Input{
Key: "VirtualType",
Desc: fmt.Sprintf("%s%s", ChooseVirtualType, createKeyValuePairs(fleet.VirtualType_name)),
}
} else if value == "n" {
mlsep.VirtualRequirements = prs
input = nil
} else {
input = &Input{
Key: "VirtualRequirements (y/n)",
Desc: fmt.Sprintf("%s%sVirtualRequirement?", WrongInput, OptionToEnter),
Required: true,
}
}
case "VirtualType":
if value == "" || value == "0" {
pr = &fleet.VirtualRequirement{}
} else {
option := getSelectionInput(value, fleet.VirtualType_name, input)
if option == -1 {
break
}
pr = &fleet.VirtualRequirement{
VirtualType: fleet.VirtualType(option),
}
}
input = &Input{
Key: "Minimum",
Desc: "The minimum/maximum number of virtual types that can be setup.",
}
case "Minimum":
if value != "" {
val := getIntInput(value, input)
if val == -1 {
break
}
pr.Min = val
}
input = &Input{
Key: "Maximum",
}
case "Maximum":
if value != "" {
val := getIntInput(value, input)
if val == -1 {
break
}
if val < pr.Min {
input.Desc = fmt.Sprintf("%s%s", WrongInput, MinMaxError)
break
}
pr.Max = val
}
prs = append(prs, pr)
input = &Input{
Key: "VirtualRequirements (y/n)",
Desc: fmt.Sprintf("%sVirtualRequirement?", OptionToEnterMore),
Required: true,
}
}
break
}
}
}
// GetChromePlatformInteractiveInput gets ChromePlatform input in interactive mode
//
// Name(string) -> Manufacturer(string) -> Description(string)
func GetChromePlatformInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, cp *fleet.ChromePlatform, update bool) {
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if !update && ChromePlatformExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !ChromePlatformExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
cp.Name = value
input = &Input{
Key: "Manufacturer",
}
case "Manufacturer":
cp.Manufacturer = value
input = &Input{
Key: "Description",
}
case "Description":
cp.Description = value
input = nil
}
break
}
}
}
// GetNicInteractiveInput get nic input in interactive mode
//
// Name(string) -> MAC Address(string) ->
// -> SwitchInterface[Switch(string, resource) -> Switch Port(int)] ->
// -> Machine name(string)
func GetNicInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, nic *fleet.Nic, update bool) string {
var machineName string
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return machineName
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if !update && NicExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !NicExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
nic.Name = value
input = &Input{
Key: "MAC Address",
}
case "MAC Address":
nic.MacAddress = value
input = &Input{
Key: "Switch",
}
// SwitchInterface
case "Switch":
if value != "" && !SwitchExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
nic.SwitchInterface = &fleet.SwitchInterface{
Switch: value,
}
if value != "" {
input = &Input{
Key: "Switch Port",
}
} else {
input = &Input{
Key: "Machine name",
Desc: "Name of the machine to associate this nic.",
}
if !update {
input.Required = true
}
}
case "Switch Port":
if value != "" {
nic.GetSwitchInterface().PortName = value
}
input = &Input{
Key: "Machine name",
Desc: "Name of the machine to associate this nic.",
}
if !update {
input.Required = true
}
case "Machine name":
if value != "" && !MachineExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
machineName = value
input = nil
}
break
}
}
return machineName
}
// GetDracInteractiveInput get drac input in interactive mode
//
// Name(string) -> Display name(string) -> MAC Address(string) ->
// -> SwitchInterface[Switch(string, resource) -> Switch Port(int)] ->
// -> Password(string)
func GetDracInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, drac *fleet.Drac, update bool) string {
var machineName string
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return machineName
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if !update && DracExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !DracExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
drac.Name = value
input = &Input{
Key: "Display name",
}
case "Display name":
drac.DisplayName = value
input = &Input{
Key: "MAC Address",
}
case "MAC Address":
drac.MacAddress = value
input = &Input{
Key: "Switch",
}
// SwitchInterface
case "Switch":
if value != "" && !SwitchExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
drac.SwitchInterface = &fleet.SwitchInterface{
Switch: value,
}
if value != "" {
input = &Input{
Key: "Switch Port",
}
} else {
input = &Input{
Key: "Password",
}
}
case "Switch Port":
if value != "" {
drac.GetSwitchInterface().PortName = value
}
input = &Input{
Key: "Password",
}
case "Password":
drac.Password = value
input = &Input{
Key: "Machine name",
Desc: "Name of the machine to associate this drac.",
}
if !update {
input.Required = true
}
case "Machine name":
if value != "" && !MachineExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
machineName = value
input = nil
}
break
}
}
return machineName
}
// GetKVMInteractiveInput get kvm input in interactive mode
//
// Name(string) -> MAC Address(string) -> ChromePlatform(string, resource) ->
// -> CapacityPort(int) -> Rack name(string)
func GetKVMInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, kvm *fleet.KVM, update bool) string {
var rackName string
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
chromePlatforms := getAllChromePlatforms(ctx, ic)
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return rackName
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if !update && KVMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
} else if update && !KVMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
kvm.Name = value
input = &Input{
Key: "MAC Address",
}
case "MAC Address":
kvm.MacAddress = value
input = &Input{
Key: "ChromePlatform",
Desc: fmt.Sprintf("%s%s", ChooseChromePlatform, createKeyValuePairs(chromePlatforms)),
}
case "ChromePlatform":
if value != "" {
option := getSelectionInput(value, chromePlatforms, input)
if option == -1 {
break
}
kvm.ChromePlatform = chromePlatforms[option]
}
input = &Input{
Key: "CapacityPort",
}
case "CapacityPort":
if value != "" {
port := getIntInput(value, input)
if port == -1 {
break
}
kvm.CapacityPort = port
}
input = &Input{
Key: "Rack name",
Desc: "Name of the rack to associate this kvm.",
}
if !update {
input.Required = true
}
case "Rack name":
if value != "" && !RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
rackName = value
input = nil
}
break
}
}
return rackName
}
// GetRPMInteractiveInput get rpm input in interactive mode
//
// Name(string) -> Rack name(string) -> MAC Address(string) -> CapacityPort(int)
func GetRPMInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, rpm *fleet.RPM) {
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if RPMExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
}
rpm.Name = value
input = &Input{
Key: "Rack name",
Desc: "Name of the rack to associate this rpm.",
Required: true,
}
case "Rack name":
if value != "" && !RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
rpm.Rack = value
input = &Input{
Key: "MAC Address",
}
case "MAC Address":
if err := UfleetUtil.IsMacFormatValid(value); err != nil {
input.Desc = err.Error()
break
}
rpm.MacAddress = value
input = &Input{
Key: "CapacityPort",
}
case "CapacityPort":
if value != "" {
port := getIntInput(value, input)
if port == -1 {
input.Desc = "Invalid number input. Please enter a valid input."
break
}
rpm.CapacityPort = port
}
input = nil
}
break
}
}
return
}
// GetRackInteractiveInput get rack input in interactive mode
//
// Name(string) -> Rack name(string) -> CapacityPort(int)
func GetRackInteractiveInput(ctx context.Context, ic UfleetAPI.FleetClient, req *UfleetAPI.RackRegistrationRequest) {
input := &Input{
Key: "Name",
Desc: UfleetAPI.ValidName,
Required: true,
}
scanner := bufio.NewScanner(os.Stdin)
fmt.Println(InputDetails)
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case "Name":
if !UfleetAPI.IDRegex.MatchString(value) {
break
}
if RackExists(ctx, ic, value) {
input.Desc = fmt.Sprintf("%s%s", value, AlreadyExists)
break
}
req.Rack = &fleet.Rack{
Name: value,
}
input = &Input{
Key: "Zone",
Desc: fmt.Sprintf("%s%s", ChooseZone, createKeyValuePairs(fleet.Zone_name)),
}
case "Zone":
option := getSelectionInput(value, fleet.Zone_name, input)
if option == -1 {
break
}
req.Rack.Location = &fleet.Location{
Zone: fleet.Zone(option),
}
if UfleetUtil.IsInBrowserZone(fleet.Zone(option).String()) {
req.Rack.Rack = &fleet.Rack_ChromeBrowserRack{
ChromeBrowserRack: &fleet.ChromeBrowserRack{},
}
} else {
req.Rack.Rack = &fleet.Rack_ChromeosRack{
ChromeosRack: &fleet.ChromeOSRack{},
}
}
req.GetRack().Realm = UfleetUtil.ToUFSRealm(fleet.Zone(option).String())
input = &Input{
Key: "Capacity_RU",
}
case "Capacity_RU":
if value != "" {
capacity := getIntInput(value, input)
if capacity == -1 {
input.Desc = "Invalid number input. Please enter a valid input."
break
}
req.Rack.CapacityRu = capacity
}
input = nil
}
break
}
}
return
}
func createKeyValuePairs(m map[int32]string) string {
keys := make([]int32, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
var s string
for _, k := range keys {
s = fmt.Sprintf("%s%d = \"%s\"\n", s, k, m[k])
}
return s
}
func getLab(lab fleet.Lab) string {
switch lab {
case fleet.Lab_LAB_CHROME_ATLANTA,
fleet.Lab_LAB_DATACENTER_ATL97,
fleet.Lab_LAB_DATACENTER_IAD97,
fleet.Lab_LAB_DATACENTER_MTV96,
fleet.Lab_LAB_DATACENTER_MTV97,
fleet.Lab_LAB_DATACENTER_FUCHSIA:
return Browser
case fleet.Lab_LAB_CHROMEOS_DESTINY,
fleet.Lab_LAB_CHROMEOS_PROMETHEUS,
fleet.Lab_LAB_CHROMEOS_ATLANTIS:
return ATL
case fleet.Lab_LAB_CHROMEOS_LINDAVISTA:
return ACS
default:
return Unknown
}
}
// getAllChromePlatforms gets all ChromePlatforms in the system
func getAllChromePlatforms(ctx context.Context, ic UfleetAPI.FleetClient) map[int32]string {
m := make(map[int32]string)
var pageToken string
var index int32
for {
req := &UfleetAPI.ListChromePlatformsRequest{
PageSize: int32(maxPageSize),
PageToken: pageToken,
}
res, err := ic.ListChromePlatforms(ctx, req)
if err != nil {
return m
}
for _, cp := range res.GetChromePlatforms() {
m[index] = UfleetUtil.RemovePrefix(cp.GetName())
index++
}
pageToken = res.GetNextPageToken()
if pageToken == "" {
break
}
}
return m
}
// ParseJSONFile parses json input from the user provided file.
func ParseJSONFile(jsonFile string, pm proto.Message) error {
rawText, err := ioutil.ReadFile(jsonFile)
if err != nil {
return errors.Annotate(err, "parse json file").Err()
}
return protojson.Unmarshal(rawText, proto.MessageV2(pm))
}
// GetNextPage gets user input for to get next page of items
func GetNextPage(pageToken string) (bool, error) {
if pageToken == "" {
fmt.Println("End of list.")
return false, nil
}
fmt.Println("Press Enter to get next page of items.")
b := bufio.NewReaderSize(os.Stdin, 1)
input, err := b.ReadByte()
if err != nil {
return false, errors.Annotate(err, "Error in getting input").Err()
}
// Ctrl-C
if input == 3 {
fmt.Println("Exiting...")
return false, nil
}
return true, nil
}
func getBoolInput(value string, input *Input) (bool, bool) {
value = strings.ToLower(value)
if value != "y" && value != "n" {
input.Desc = fmt.Sprintf("%s", WrongInput)
return false, false
}
// User has entered some valid input so we can go to the next input field
if value == "y" {
return true, true
}
return false, true
}
func getIntInput(value string, input *Input) int32 {
i, err := strconv.ParseInt(value, 10, 32)
if err != nil {
input.Desc = fmt.Sprintf("%s", WrongInput)
return -1
}
return int32(i)
}
func getSelectionInput(value string, m map[int32]string, input *Input) int32 {
i, err := strconv.ParseInt(value, 10, 32)
if err != nil {
input.Desc = fmt.Sprintf("%s%s%s", WrongInput, "\nChoose a "+input.Key+"\n", createKeyValuePairs(m))
return -1
}
option := int32(i)
_, ok := m[option]
if !ok {
input.Desc = fmt.Sprintf("%s%s%s", WrongInput, "\nChoose a "+input.Key+"\n", createKeyValuePairs(m))
return -1
}
return option
}
func getRepeatedStringInput(ctx context.Context, ic UfleetAPI.FleetClient, scanner *bufio.Scanner, yn, key string, input *Input, isResource bool) ([]string, bool) {
yn = strings.ToLower(yn)
if yn != "y" && yn != "n" {
input.Desc = fmt.Sprintf("%s%s%s?", WrongInput, OptionToEnter, input.Key)
return nil, false
}
if yn == "n" {
return nil, true
}
values := make([]string, 0, 0)
input = &Input{
Key: key,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return values, true
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case key + YesNo:
value = strings.ToLower(value)
if value == "y" {
input = &Input{
Key: key,
}
} else if value == "n" {
input = nil
} else {
input.Desc = fmt.Sprintf("%s%s%s?", WrongInput, OptionToEnter, key)
}
case key:
if isResource && value != "" && !EntityExists(ctx, ic, key, value) {
input.Desc = fmt.Sprintf("%s%s", value, DoesNotExist)
break
}
if value != "" {
values = append(values, value)
}
input = &Input{
Key: key + YesNo,
Desc: fmt.Sprintf("%s%s?", OptionToEnterMore, key),
Required: true,
}
}
break
}
}
return values, true
}
func getRepeatedEnumInput(scanner *bufio.Scanner, yn, key string, m map[int32]string, input *Input) ([]int32, bool) {
yn = strings.ToLower(yn)
if yn != "y" && yn != "n" {
input.Desc = fmt.Sprintf("%s%s%s?", WrongInput, OptionToEnter, key)
return nil, false
}
if yn == "n" {
return nil, true
}
values := make([]int32, 0, 0)
input = &Input{
Key: key,
Desc: fmt.Sprintf("%s%s", "\nChoose a "+key+"\n", createKeyValuePairs(m)),
Required: true,
}
for input != nil {
if input.Desc != "" {
fmt.Println(input.Desc)
}
fmt.Print(input.Key, ": ")
for true {
if !scanner.Scan() {
return values, true
}
value := scanner.Text()
if value == "" && input.Required {
fmt.Println(input.Key, RequiredField)
fmt.Print(input.Key, ": ")
continue
}
switch input.Key {
case key + YesNo:
value = strings.ToLower(value)
if value == "y" {
input = &Input{
Key: key,
Desc: fmt.Sprintf("%s%s", "\nChoose a "+key+"\n", createKeyValuePairs(m)),
Required: true,
}
} else if value == "n" {
input = nil
} else {
input.Desc = fmt.Sprintf("%s%s%s?", WrongInput, OptionToEnter, key)
}
case key:
option := getSelectionInput(value, m, input)
if option == -1 {
break
}
values = append(values, option)
input = &Input{
Key: key + YesNo,
Desc: fmt.Sprintf("%s%s?", OptionToEnterMore, key),
Required: true,
}
}
break
}
}
return values, true
}
// getAllMachineLSEPrototypes gets all MachineLSEPrototypes in the system
func getAllMachineLSEPrototypes(ctx context.Context, ic UfleetAPI.FleetClient, lab string) map[int32]string {
m := make(map[int32]string)
var pageToken string
var index int32
for {
req := &UfleetAPI.ListMachineLSEPrototypesRequest{
PageSize: int32(maxPageSize),
PageToken: pageToken,
}
res, err := ic.ListMachineLSEPrototypes(ctx, req)
if err != nil {
return m
}
for _, cp := range res.GetMachineLSEPrototypes() {
name := UfleetUtil.RemovePrefix(cp.GetName())
if strings.Contains(name, lab) {
m[index] = name
index++
}
}
pageToken = res.GetNextPageToken()
if pageToken == "" {
break
}
}
return m
}