SEBA-580 Add bash completion;
Add Git Dirty to version;
Add config comamnd

Change-Id: I847dc309a41d74d80e427106bcc839b8db3f9a2c
diff --git a/Makefile b/Makefile
index 297c16b..c7ca017 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,11 @@
 
 VERSION=$(shell cat $(GOPATH)/src/github.com/opencord/cordctl/VERSION)
 GITCOMMIT=$(shell git log --pretty=format:"%h" -n 1)
+ifeq ($(shell git ls-files --others --modified --exclude-standard 2>/dev/null | wc -l | sed -e 's/ //g'),0)
+GITDIRTY=false
+else
+GITDIRTY=true
+endif
 GOVERSION=$(shell go version 2>&1 | sed -E  's/.*(go[0-9]+\.[0-9]+\.[0-9]+).*/\1/g')
 OSTYPE=$(shell uname -s | tr A-Z a-z)
 OSARCH=$(shell uname -p | tr A-Z a-z)
@@ -12,10 +17,11 @@
 LDFLAGS=-ldflags \
 	'-X "github.com/opencord/cordctl/cli/version.Version=$(VERSION)"  \
 	 -X "github.com/opencord/cordctl/cli/version.GitCommit=$(GITCOMMIT)"  \
+	 -X "github.com/opencord/cordctl/cli/version.GitDirty=$(GITDIRTY)"  \
 	 -X "github.com/opencord/cordctl/cli/version.GoVersion=$(GOVERSION)"  \
 	 -X "github.com/opencord/cordctl/cli/version.Os=$(OSTYPE)" \
 	 -X "github.com/opencord/cordctl/cli/version.Arch=$(OSARCH)" \
-	 -X "github.com/opencord/cordctl/cli/version.BuildTime=$(BUILDTIME)"' \
+	 -X "github.com/opencord/cordctl/cli/version.BuildTime=$(BUILDTIME)"'
 
 help:
 
diff --git a/README.md b/README.md
index 346a244..22361e6 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,26 @@
 ./cordctl model list -h
 ```
 
+## Shell Completion
+`cordctl` supports shell completion for the `bash` shell. To enable
+shell Completion you can use the following command on *most* \*nix based system.
+```bash
+source <(cordctl completion bash)
+```
+
+If this does not work on your system, as is the case with the standard
+bash shell on MacOS, then you can try the following command:
+```bash
+source /dev/stdin <<<"$(cordctl completion bash)"
+```
+
+If you which to make `bash` shell completion automatic when you login to
+your account you can append the output of `cordctl completion bash` to
+your `$HOME/.bashrc`:
+```bash
+cordctl completion bash >> $HOME/.bashrc
+```
+
 ## Development Environment
 
 To run unit tests, `go-junit-report` and `gocover-obertura` tools must be installed. One way to do this is to install them with `go get`, and then ensure your `GOPATH` is part of your `PATH` (editing your `~/.profile` as necessary). 
diff --git a/cli/version/version.go b/cli/version/version.go
index dcae592..21880bb 100644
--- a/cli/version/version.go
+++ b/cli/version/version.go
@@ -22,7 +22,8 @@
 var (
 	Version   = "unknown-version"
 	GoVersion = "unknown-goversion"
-	GitCommit = "unknown-commit"
+	GitCommit = "unknown-gitcommit"
+	GitDirty  = "unknown-gitdirty"
 	BuildTime = "unknown-buildtime"
 	Os        = "unknown-os"
 	Arch      = "unknown-arch"
diff --git a/cmd/cordctl.go b/cmd/cordctl.go
index b4e3c0d..53daf9b 100644
--- a/cmd/cordctl.go
+++ b/cmd/cordctl.go
@@ -35,6 +35,8 @@
 	commands.RegisterServiceCommands(parser)
 	commands.RegisterTransferCommands(parser)
 	commands.RegisterVersionCommands(parser)
+	commands.RegisterCompletionCommands(parser)
+	commands.RegisterConfigCommands(parser)
 
 	_, err = parser.ParseArgs(os.Args[1:])
 	if err != nil {
diff --git a/commands/command.go b/commands/command.go
index 9741e51..6a8d663 100644
--- a/commands/command.go
+++ b/commands/command.go
@@ -111,16 +111,12 @@
 	Server string `yaml:"server"`
 }
 
-func NewConnection() (*grpc.ClientConn, error) {
+func ProcessGlobalOptions() {
 	if len(GlobalOptions.Config) == 0 {
-		home := os.Getenv("HOME")
-		// TODO: Replace after Jenkins updated to go 1.12
-		/*
-			home, err := os.UserHomeDir()
-			if err != nil {
-				log.Printf("Unable to discover they users home directory: %s\n", err)
-			}
-		*/
+		home, err := os.UserHomeDir()
+		if err != nil {
+			log.Printf("Unable to discover the users home directory: %s\n", err)
+		}
 		GlobalOptions.Config = fmt.Sprintf("%s/.cord/config", home)
 	}
 
@@ -147,6 +143,19 @@
 		GlobalConfig.Password = GlobalOptions.Password
 	}
 
+	if GlobalConfig.Server == "" {
+		log.Fatal("Server is not set. Please update config file or use the -s option")
+	}
+	if GlobalConfig.Username == "" {
+		log.Fatal("Username is not set. Please update config file or use the -u option")
+	}
+	if GlobalConfig.Password == "" {
+		log.Fatal("Password is not set. Please update config file or use the -p option")
+	}
+}
+
+func NewConnection() (*grpc.ClientConn, error) {
+	ProcessGlobalOptions()
 	return grpc.Dial(GlobalConfig.Server, grpc.WithInsecure())
 }
 
diff --git a/commands/completion.go b/commands/completion.go
new file mode 100644
index 0000000..67c7ad3
--- /dev/null
+++ b/commands/completion.go
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019-present Ciena Corporation
+ *
+ * Licensed 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 commands
+
+import (
+	"fmt"
+	flags "github.com/jessevdk/go-flags"
+	"github.com/opencord/cordctl/completion"
+)
+
+type BashOptions struct{}
+
+type CompletionOptions struct {
+	BashOptions `command:"bash"`
+}
+
+func RegisterCompletionCommands(parent *flags.Parser) {
+	parent.AddCommand("completion", "generate shell compleition", "Commands to generate shell completion information", &CompletionOptions{})
+}
+
+func (options *BashOptions) Execute(args []string) error {
+	fmt.Println(completion.Bash)
+	return nil
+}
diff --git a/commands/config.go b/commands/config.go
new file mode 100644
index 0000000..81875a5
--- /dev/null
+++ b/commands/config.go
@@ -0,0 +1,60 @@
+/*
+ * Portions copyright 2019-present Open Networking Foundation
+ * Original copyright 2019-present Ciena Corporation
+ *
+ * Licensed 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 commands
+
+import (
+	"fmt"
+	flags "github.com/jessevdk/go-flags"
+	"gopkg.in/yaml.v2"
+)
+
+const copyrightNotice = `
+# Portions copyright 2019-present Open Networking Foundation
+# Original copyright 2019-present Ciena Corporation
+#
+# Licensed 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.
+#
+`
+
+type ConfigOptions struct {
+}
+
+func RegisterConfigCommands(parent *flags.Parser) {
+	parent.AddCommand("config", "generate cordctl configuration", "Commands to generate cordctl configuration", &ConfigOptions{})
+}
+
+func (options *ConfigOptions) Execute(args []string) error {
+	//GlobalConfig
+	ProcessGlobalOptions()
+	b, err := yaml.Marshal(GlobalConfig)
+	if err != nil {
+		return err
+	}
+	fmt.Println(copyrightNotice)
+	fmt.Println(string(b))
+	return nil
+}
diff --git a/commands/models.go b/commands/models.go
index 92a7b2c..f7a1963 100644
--- a/commands/models.go
+++ b/commands/models.go
@@ -31,13 +31,15 @@
 	DEFAULT_MODEL_AVAILABLE_FORMAT = "{{ . }}"
 )
 
+type ModelNameString string
+
 type ModelList struct {
 	OutputOptions
 	ShowHidden      bool `long:"showhidden" description:"Show hidden fields in default output"`
 	ShowFeedback    bool `long:"showfeedback" description:"Show feedback fields in default output"`
 	ShowBookkeeping bool `long:"showbookkeeping" description:"Show bookkeeping fields in default output"`
 	Args            struct {
-		ModelName string
+		ModelName ModelNameString
 	} `positional-args:"yes" required:"yes"`
 }
 
@@ -100,12 +102,12 @@
 
 	defer conn.Close()
 
-	err = CheckModelName(descriptor, options.Args.ModelName)
+	err = CheckModelName(descriptor, string(options.Args.ModelName))
 	if err != nil {
 		return err
 	}
 
-	method := "xos.xos/List" + options.Args.ModelName
+	method := "xos.xos/List" + string(options.Args.ModelName)
 
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
 	defer cancel()
@@ -195,3 +197,26 @@
 	GenerateOutput(&result)
 	return nil
 }
+
+func (modelName *ModelNameString) Complete(match string) []flags.Completion {
+	conn, descriptor, err := InitReflectionClient()
+	if err != nil {
+		return nil
+	}
+
+	defer conn.Close()
+
+	models, err := GetModelNames(descriptor)
+	if err != nil {
+		return nil
+	}
+
+	list := make([]flags.Completion, 0)
+	for k := range models {
+		if strings.HasPrefix(k, match) {
+			list = append(list, flags.Completion{Item: k})
+		}
+	}
+
+	return list
+}
diff --git a/commands/version.go b/commands/version.go
index 9cc8b05..4af5a8a 100644
--- a/commands/version.go
+++ b/commands/version.go
@@ -29,6 +29,7 @@
 	Version   string `json:"version"`
 	GoVersion string `json:"goversion"`
 	GitCommit string `json:"gitcommit"`
+	GitDirty  string `json:"gitdirty"`
 	BuildTime string `json:"buildtime"`
 	Os        string `json:"os"`
 	Arch      string `json:"arch"`
@@ -59,6 +60,7 @@
 		Version:   version.Version,
 		GoVersion: version.GoVersion,
 		GitCommit: version.GitCommit,
+		GitDirty:  version.GitDirty,
 		Os:        version.Os,
 		Arch:      version.Arch,
 		BuildTime: version.BuildTime,
@@ -81,6 +83,7 @@
  Version         {{.Client.Version}}
  Go version:     {{.Client.GoVersion}}
  Git commit:     {{.Client.GitCommit}}
+ Git dirty:      {{.Client.GitDirty}}
  Built:          {{.Client.BuildTime}}
  OS/Arch:        {{.Client.Os}}/{{.Client.Arch}}
 
diff --git a/completion/bash.go b/completion/bash.go
new file mode 100644
index 0000000..4feb2a2
--- /dev/null
+++ b/completion/bash.go
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019-present Ciena Corporation
+ *
+ * Licensed 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 completion
+
+const Bash = `
+# Portions copyright 2019-present Open Networking Foundation
+# Original copyright 2019-present Ciena Corporation
+#
+# Licensed 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.
+#
+_cordctl() {
+    # All arguments except the first one
+    args=("${COMP_WORDS[@]:1:$COMP_CWORD}")
+    # Only split on newlines
+    local IFS=$'\n'
+    # Call completion (note that the first element of COMP_WORDS is
+    # the executable itself)
+    COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
+    return 0
+}
+complete -F _cordctl cordctl
+`