feature: add switch profile command (#192)

Fixes #182

Adds a switch profile command to safely load an existing profile without
creating a new profile.

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
diff --git a/cmd/switch.go b/cmd/switch.go
new file mode 100644
index 0000000..2fd8d61
--- /dev/null
+++ b/cmd/switch.go
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package cmd
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/apache/cloudstack-cloudmonkey/config"
+)
+
+func init() {
+	AddCommand(&Command{
+		Name: "switch",
+		Help: "Switches profile",
+		SubCommands: map[string][]string{
+			"profile": {},
+		},
+		Handle: func(r *Request) error {
+			if len(r.Args) < 1 {
+				fmt.Println("Please provide one of the sub-commands: ", reflect.ValueOf(r.Command.SubCommands).MapKeys())
+				return nil
+			}
+			subCommand := r.Args[0]
+			value := strings.Trim(strings.Join(r.Args[1:], " "), " ")
+			config.Debug("Switch command received:", subCommand, " values:", value)
+			if r.Args[len(r.Args)-1] == "-h" {
+				fmt.Println("Usage: switch <subcommand> <option>. Press tab-tab to see available subcommands and options.")
+				return nil
+			}
+			validArgs := r.Command.SubCommands[subCommand]
+			if len(validArgs) != 0 {
+				if !config.CheckIfValuePresent(validArgs, value) {
+					return errors.New("Invalid value for " + subCommand + ". Supported values: " + strings.Join(validArgs, ", "))
+				}
+			}
+
+			if subCommand == "profile" {
+				if err := r.Config.LoadProfile(value, false); err != nil {
+					if r.Config.HasShell {
+						fmt.Printf("Failed to switch to server profile: %s due to: %v\n", value, err)
+						return nil
+					}
+					return err
+				}
+				if r.Config.HasShell {
+					ap := r.Config.ActiveProfile
+					fmt.Printf("Loaded server profile: %s\nUrl:        %s\nUsername:   %s\nDomain:     %s\nAPI Key:    %s\nTotal APIs: %d\n\n",
+						r.Config.Core.ProfileName, ap.URL, ap.Username, ap.Domain, ap.APIKey, len(r.Config.GetCache()))
+				}
+			}
+			return nil
+		},
+	})
+}
diff --git a/cmk.go b/cmk.go
index c2ae652..9ba8073 100644
--- a/cmk.go
+++ b/cmk.go
@@ -85,7 +85,7 @@
 	}
 
 	if *profile != "" {
-		cfg.LoadProfile(*profile)
+		cfg.LoadProfile(*profile, true)
 	}
 	config.LoadCache(cfg)
 	cli.SetConfig(cfg)
diff --git a/config/config.go b/config/config.go
index cf32b69..1fb06d8 100644
--- a/config/config.go
+++ b/config/config.go
@@ -340,18 +340,22 @@
 }
 
 // LoadProfile loads an existing profile
-func (c *Config) LoadProfile(name string) {
+func (c *Config) LoadProfile(name string, isExit bool) error {
 	Debug("Trying to load profile: " + name)
 	conf := readConfig(c)
 	section, err := conf.GetSection(name)
 	if err != nil || section == nil {
-		fmt.Printf("Unable to load profile '%s': %v", name, err)
-		os.Exit(1)
+		if isExit {
+			fmt.Printf("Unable to load profile '%s': %v", name, err)
+			os.Exit(1)
+		}
+		return err
 	}
 	profile := new(ServerProfile)
 	conf.Section(name).MapTo(profile)
 	setActiveProfile(c, profile)
 	c.Core.ProfileName = name
+	return nil
 }
 
 // UpdateConfig updates and saves config