Esempio n. 1
0
// Track the progress of the task, returns an error if one occurred
func monitorTask(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args()[0]

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		task, err := client.Esxclient.Tasks.Wait(id)
		if err != nil {
			return err
		}
		fmt.Printf("%s\t%s\t%s\t%s\n", task.ID, task.State, task.Entity.ID, task.Entity.Kind)
	} else {
		task, err := pollTask(id)
		if err != nil {
			return err
		}
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "Task:\t%s\n", task.ID)
		fmt.Fprintf(w, "Entity:\t%s %s\n", task.Entity.Kind, task.Entity.ID)
		fmt.Fprintf(w, "State:\t%s\n", task.State)
		err = w.Flush()
		if err != nil {
			return err
		}
	}
	return nil
}
Esempio n. 2
0
func detachDisk(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()
	diskID := c.String("disk")

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	operation := &photon.VmDiskOperation{
		DiskID: diskID,
	}

	task, err := client.Esxclient.VMs.DetachDisk(id, operation)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	err = formatHelper(c, w, client.Esxclient, id)

	return err
}
Esempio n. 3
0
// Sends a cluster trigger_maintenance request to the API client based on the cli.Context.
// Returns an error if one occurred.
func triggerMaintenance(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return nil
	}

	clusterId := c.Args().First()

	if len(clusterId) == 0 {
		return fmt.Errorf("Please provide a valid cluster ID")
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	if !c.GlobalIsSet("non-interactive") {
		fmt.Printf("Maintenance triggered for cluster %s\n", clusterId)
	}

	task, err := client.Esxclient.Clusters.TriggerMaintenance(clusterId)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	return nil
}
func showPhysicalNetwork(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	network, err := client.Esxclient.Subnets.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		portGroups := getCommaSeparatedStringFromStringArray(network.PortGroups)
		fmt.Printf("%s\t%s\t%s\t%s\t%s\t%t\n", network.ID, network.Name, network.State, portGroups,
			network.Description, network.IsDefault)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(network, w, c)
	} else {
		fmt.Printf("Network ID: %s\n", network.ID)
		fmt.Printf("  Name:        %s\n", network.Name)
		fmt.Printf("  State:       %s\n", network.State)
		fmt.Printf("  Description: %s\n", network.Description)
		fmt.Printf("  Port Groups: %s\n", network.PortGroups)
		fmt.Printf("  Is Default: %t\n", network.IsDefault)
	}

	return nil
}
Esempio n. 5
0
// Sends a delete project task to client based on the cli.Context
// Returns an error if one occurred
func deleteProject(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	deleteTask, err := client.Esxclient.Projects.Delete(id)
	if err != nil {
		return err
	}
	_, err = waitOnTaskOperation(deleteTask.ID, c)
	if err != nil {
		return err
	}

	err = clearConfigProject(id)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 6
0
// Starts the recurring copy state of source system into destination
func deploymentMigrationPrepareDeprecated(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	sourceAddress := c.Args().First()
	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}
	deployments, err := client.Esxclient.Deployments.GetAll()
	if err != nil {
		return err
	}
	initializeMigrationSpec := photon.InitializeMigrationOperation{}
	initializeMigrationSpec.SourceNodeGroupReference = sourceAddress

	// Initialize deployment migration
	for _, deployment := range deployments.Items {
		initializeMigrate, err := client.Esxclient.Deployments.InitializeDeploymentMigration(&initializeMigrationSpec, deployment.ID)
		if err != nil {
			return err
		}
		_, err = pollTask(initializeMigrate.ID)
		if err != nil {
			return err
		}
		fmt.Printf("Deployment '%s' migration started [source management endpoint: '%s'].\n", deployment.ID, sourceAddress)

		return nil
	}

	return nil
}
Esempio n. 7
0
// displays the migration status
func showMigrationStatusDeprecated(c *cli.Context) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}
	deployments, err := client.Esxclient.Deployments.GetAll()
	if err != nil {
		return err
	}

	for _, deployment := range deployments.Items {
		if deployment.Migration != nil {
			migration := deployment.Migration
			if c.GlobalIsSet("non-interactive") {
				fmt.Printf("%d\t%d\t%d\t%d\t%d\n", migration.CompletedDataMigrationCycles, migration.DataMigrationCycleProgress,
					migration.DataMigrationCycleSize, migration.VibsUploaded, migration.VibsUploading+migration.VibsUploaded)
			} else {
				fmt.Printf("  Migration status:\n")
				fmt.Printf("    Completed data migration cycles:          %d\n", migration.CompletedDataMigrationCycles)
				fmt.Printf("    Current data migration cycles progress:   %d / %d\n", migration.DataMigrationCycleProgress,
					migration.DataMigrationCycleSize)
				fmt.Printf("    VIB upload progress:                      %d / %d\n", migration.VibsUploaded, migration.VibsUploading+migration.VibsUploaded)
			}
		}
		return nil
	}
	return nil
}
Esempio n. 8
0
func detachIso(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.VMs.DetachISO(id)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	err = formatHelper(c, w, client.Esxclient, id)

	return err
}
Esempio n. 9
0
func listVMNetworks(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	networks, err := getVMNetworks(id, c)
	if err != nil {
		return err
	}

	if !utils.NeedsFormatting(c) {
		err = printVMNetworks(networks, c.GlobalIsSet("non-interactive"))
		if err != nil {
			return err
		}
	} else {
		utils.FormatObjects(networks, w, c)
	}
	return nil
}
Esempio n. 10
0
// Sends a pause background task to client
func pauseBackgroundTasks(c *cli.Context) error {
	id, err := getDeploymentId(c)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	pauseBackgroundTask, err := client.Esxclient.Deployments.PauseBackgroundTasks(id)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(pauseBackgroundTask.ID, c)
	if err != nil {
		return err
	}

	err = deploymentJsonHelper(c, id, client.Esxclient)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 11
0
// Sends a resume system task to client
func resumeSystem(c *cli.Context) error {
	id, err := getDeploymentId(c)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	resumeSystemTask, err := client.Esxclient.Deployments.ResumeSystem(id)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(resumeSystemTask.ID, c)
	if err != nil {
		return err
	}

	err = deploymentJsonHelper(c, id, client.Esxclient)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 12
0
// Synchronizes hosts configurations
func syncHostsConfig(c *cli.Context) error {
	id, err := getDeploymentId(c)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.Deployments.SyncHostsConfig(id)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	err = deploymentJsonHelper(c, id, client.Esxclient)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 13
0
// Lists all the hosts associated with the deployment
func listDeploymentVms(c *cli.Context, w io.Writer) error {
	id, err := getDeploymentId(c)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	vms, err := client.Esxclient.Deployments.GetVms(id)
	if err != nil {
		return err
	}

	if utils.NeedsFormatting(c) {
		utils.FormatObjects(vms, w, c)
	} else {
		err = printVMList(vms.Items, os.Stdout, c, false)
		if err != nil {
			return err
		}
	}

	return nil
}
Esempio n. 14
0
// Retrieves a list of tasks, returns an error if one occurred
func listTasks(c *cli.Context) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	entityId := c.String("entityId")
	entityKind := c.String("entityKind")
	state := c.String("state")

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	options := &photon.TaskGetOptions{
		State:      state,
		EntityID:   entityId,
		EntityKind: entityKind,
	}
	taskList, err := client.Esxclient.Tasks.GetAll(options)
	if err != nil {
		return err
	}

	err = printTaskList(taskList.Items, c)
	if err != nil {
		return err
	}
	return nil
}
Esempio n. 15
0
// Deploy Photon Controller based on DC_map
func deploy(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	file := c.Args().First()
	dcMap, err := manifest.LoadInstallation(file)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	deploymentID, err := createDeploymentFromDcMap(dcMap)
	if err != nil {
		return err
	}

	// Create Hosts
	err = createHostsFromDcMap(dcMap, deploymentID)
	if err != nil {
		return err
	}

	// Deploy
	err = doDeploy(dcMap, deploymentID)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 16
0
func setVMTag(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()

	tag := c.String("tag")
	vmTag := &photon.VmTag{}

	if len(tag) == 0 {
		return fmt.Errorf("Please input a tag")
	}
	vmTag.Tag = tag

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.VMs.SetTag(id, vmTag)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	err = formatHelper(c, w, client.Esxclient, id)

	return err
}
Esempio n. 17
0
// Add most hosts in batch mode
func addHosts(c *cli.Context) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	file := c.Args().First()
	dcMap, err := manifest.LoadInstallation(file)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	deployments, err := client.Esxclient.Deployments.GetAll()
	deploymentID := deployments.Items[0].ID

	// Create Hosts
	err = createHostsInBatch(dcMap, deploymentID)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 18
0
func acquireFloatingIp(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()
	networkId := c.String("network_id")

	options := &photon.VmFloatingIpSpec{
		NetworkId: networkId,
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.VMs.AcquireFloatingIp(id, options)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	err = formatHelper(c, w, client.Esxclient, id)

	return err
}
Esempio n. 19
0
// Finishes the copy state of source system into destination and makes this system the active one
func deploymentMigrationFinalizeDeprecated(c *cli.Context) error {
	fmt.Printf("'%d'", len(c.Args()))
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	sourceAddress := c.Args().First()
	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}
	deployments, err := client.Esxclient.Deployments.GetAll()
	if err != nil {
		return err
	}
	finalizeMigrationSpec := photon.FinalizeMigrationOperation{}
	finalizeMigrationSpec.SourceNodeGroupReference = sourceAddress

	// Finalize deployment migration
	for _, deployment := range deployments.Items {
		finalizeMigrate, err := client.Esxclient.Deployments.FinalizeDeploymentMigration(&finalizeMigrationSpec, deployment.ID)
		if err != nil {
			return err
		}
		_, err = pollTask(finalizeMigrate.ID)
		if err != nil {
			return err
		}

		return nil
	}

	return nil
}
Esempio n. 20
0
// Retrieves tasks for VM
func getVMTasks(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()
	state := c.String("state")

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	options := &photon.TaskGetOptions{
		State: state,
	}

	taskList, err := client.Esxclient.VMs.GetTasks(id, options)
	if err != nil {
		return err
	}

	if !utils.NeedsFormatting(c) {
		err = printTaskList(taskList.Items, c)
		if err != nil {
			return err
		}
	} else {
		utils.FormatObjects(taskList, w, c)
	}

	return nil
}
Esempio n. 21
0
// Deletes an image by id
func deleteImage(c *cli.Context) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	if confirmed(c) {
		client.Esxclient, err = client.GetClient(c)
		if err != nil {
			return err
		}

		deleteTask, err := client.Esxclient.Images.Delete(id)
		if err != nil {
			return err
		}

		_, err = waitOnTaskOperation(deleteTask.ID, c)
		if err != nil {
			return err
		}
	} else {
		fmt.Println("OK, canceled")
	}

	return nil
}
Esempio n. 22
0
func restartVM(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}

	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	opTask, err := client.Esxclient.VMs.Restart(id)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(opTask.ID, c)
	if err != nil {
		return err
	}

	err = formatHelper(c, w, client.Esxclient, id)

	return err
}
// Retrieves availability zone against specified id.
func showAvailabilityZone(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	zone, err := client.Esxclient.AvailabilityZones.Get(id)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		fmt.Printf("%s\t%s\t%s\t%s\n", zone.ID, zone.Name, zone.Kind, zone.State)
	} else if utils.NeedsFormatting(c) {
		utils.FormatObject(zone, w, c)
	} else {
		fmt.Printf("AvailabilityZone ID: %s\n", zone.ID)
		fmt.Printf("  Name:        %s\n", zone.Name)
		fmt.Printf("  Kind:        %s\n", zone.Kind)
		fmt.Printf("  State:       %s\n", zone.State)
	}

	return nil
}
Esempio n. 24
0
// Get endpoint in config file and its status
func getStatus(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	status, err := client.Esxclient.Status.Get()
	if err != nil {
		return err
	}

	if !utils.NeedsFormatting(c) {
		err = printStatus(status)
	} else {
		utils.FormatObject(status, w, c)
	}
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 25
0
func getHostTasks(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 1)
	if err != nil {
		return err
	}
	id := c.Args().First()

	state := c.String("state")
	options := &photon.TaskGetOptions{
		State: state,
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	taskList, err := client.Esxclient.Hosts.GetTasks(id, options)
	if err != nil {
		return err
	}

	err = printTaskList(taskList.Items, c)
	if err != nil {
		return err
	}
	return nil
}
Esempio n. 26
0
// Set security groups for a tenant
func setSecurityGroups(c *cli.Context) error {
	err := checkArgCount(c, 2)
	if err != nil {
		return err
	}
	id := c.Args().First()
	items := []string{}
	if c.Args()[1] != "" {
		items = regexp.MustCompile(`\s*,\s*`).Split(c.Args()[1], -1)
	}
	securityGroups := &photon.SecurityGroupsSpec{
		Items: items,
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.Tenants.SetSecurityGroups(id, securityGroups)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 27
0
// Set host's availability zone with the specified host ID, returns an error if one occurred
func setHostAvailabilityZone(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 2)
	if err != nil {
		return err
	}
	id := c.Args().First()
	availabilityZoneId := c.Args()[1]

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	setAvailabilityZoneSpec := photon.HostSetAvailabilityZoneOperation{}
	setAvailabilityZoneSpec.AvailabilityZoneId = availabilityZoneId
	setTask, err := client.Esxclient.Hosts.SetAvailabilityZone(id, &setAvailabilityZoneSpec)
	if err != nil {
		return err
	}
	id, err = waitOnTaskOperation(setTask.ID, c)
	if err != nil {
		return err
	}
	if utils.NeedsFormatting(c) {
		host, err := client.Esxclient.Hosts.Get(id)
		if err != nil {
			return err
		}
		utils.FormatObject(host, w, c)
	}

	return nil
}
Esempio n. 28
0
// Set security groups for a project
func setSecurityGroupsForProject(c *cli.Context) error {
	err := checkArgCount(c, 2)
	if err != nil {
		return err
	}
	id := c.Args().First()
	securityGroups := &photon.SecurityGroupsSpec{
		Items: regexp.MustCompile(`\s*,\s*`).Split(c.Args()[1], -1),
	}
	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	task, err := client.Esxclient.Projects.SetSecurityGroups(id, securityGroups)
	if err != nil {
		return err
	}

	_, err = waitOnTaskOperation(task.ID, c)
	if err != nil {
		return err
	}

	return nil
}
Esempio n. 29
0
// List all the hosts in the current deployment
// This uses the same back-end code as "deployments list-hosts", but we look up the
// deployment ID so that users don't have to specify it. In most or all installations,
// there will not be more than one deployment ID.
func listHosts(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	// Find the current deployment
	deployments, err := client.Esxclient.Deployments.GetAll()
	if err != nil {
		return err
	}
	numDeployments := len(deployments.Items)
	if numDeployments == 0 {
		return fmt.Errorf("There are no deployments, so the hosts cannot be listed.")
	} else if numDeployments > 1 {
		return fmt.Errorf("There are multiple deployments, which normally should not happen. Use deployments list-hosts.")
	}
	id := deployments.Items[0].ID

	hosts, err := client.Esxclient.Deployments.GetHosts(id)
	if err != nil {
		return err
	}

	err = printHostList(hosts.Items, w, c)
	if err != nil {
		return err
	}
	return nil
}
Esempio n. 30
0
// Retrieves a list of projects, returns an error if one occurred
func listProjects(c *cli.Context, w io.Writer) error {
	err := checkArgCount(c, 0)
	if err != nil {
		return err
	}
	tenantName := c.String("tenant")

	client.Esxclient, err = client.GetClient(c)
	if err != nil {
		return err
	}

	tenant, err := verifyTenant(tenantName)
	if err != nil {
		return err
	}

	projects, err := client.Esxclient.Tenants.GetProjects(tenant.ID, nil)
	if err != nil {
		return err
	}

	if c.GlobalIsSet("non-interactive") {
		for _, t := range projects.Items {
			limits := quotaLineItemListToString(t.ResourceTicket.Limits)
			usage := quotaLineItemListToString(t.ResourceTicket.Usage)
			fmt.Printf("%s\t%s\t%s\t%s\n", t.ID, t.Name, limits, usage)
		}
	} else if utils.NeedsFormatting(c) {
		utils.FormatObjects(projects.Items, w, c)
	} else {
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 4, 4, 2, ' ', 0)
		fmt.Fprintf(w, "ID\tName\tLimit\tUsage\n")
		for _, t := range projects.Items {
			rt := t.ResourceTicket
			for i := 0; i < len(rt.Limits); i++ {
				if i == 0 {
					fmt.Fprintf(w, "%s\t%s\t%s %g %s\t%s %g %s\n", t.ID, t.Name,
						rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit,
						rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit)
				} else {
					fmt.Fprintf(w, "\t\t%s %g %s\t%s %g %s\n",
						rt.Limits[i].Key, rt.Limits[i].Value, rt.Limits[i].Unit,
						rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit)
				}
			}
			for i := len(rt.Limits); i < len(rt.Usage); i++ {
				fmt.Fprintf(w, "\t\t\t%s %g %s\n", rt.Usage[i].Key, rt.Usage[i].Value, rt.Usage[i].Unit)
			}
		}
		err := w.Flush()
		if err != nil {
			return err
		}
		fmt.Printf("\nTotal projects: %d\n", len(projects.Items))
	}
	return nil
}