[VOL-1414] Update voltha CLI to support dynamic logging.  This
update also cleans up the log library as well as remove XPON
from the CLI.

Change-Id: Ife44abdcca7ac078f29db297d94a4b96f66b1514
diff --git a/common/log/log.go b/common/log/log.go
index 0128b7d..408158a 100644
--- a/common/log/log.go
+++ b/common/log/log.go
@@ -125,7 +125,7 @@
 	parent *zp.Logger
 }
 
-func parseAtomicLevel(l int) zp.AtomicLevel {
+func intToAtomicLevel(l int) zp.AtomicLevel {
 	switch l {
 	case DebugLevel:
 		return zp.NewAtomicLevelAt(zc.DebugLevel)
@@ -143,12 +143,12 @@
 	return zp.NewAtomicLevelAt(zc.ErrorLevel)
 }
 
-func parseLevel(l int) zc.Level {
+func intToLevel(l int) zc.Level {
 	switch l {
 	case DebugLevel:
 		return zc.DebugLevel
 	case InfoLevel:
-		return InfoLevel
+		return zc.InfoLevel
 	case WarnLevel:
 		return zc.WarnLevel
 	case ErrorLevel:
@@ -161,9 +161,28 @@
 	return zc.ErrorLevel
 }
 
+func levelToInt(l zc.Level) int {
+	switch l {
+	case zc.DebugLevel:
+		return DebugLevel
+	case zc.InfoLevel:
+		return InfoLevel
+	case zc.WarnLevel:
+		return WarnLevel
+	case zc.ErrorLevel:
+		return ErrorLevel
+	case zc.PanicLevel:
+		return PanicLevel
+	case FatalLevel:
+		return FatalLevel
+	}
+	return ErrorLevel
+}
+
+
 func getDefaultConfig(outputType string, level int, defaultFields Fields) zp.Config {
 	return zp.Config{
-		Level:            parseAtomicLevel(level),
+		Level:            intToAtomicLevel(level),
 		Encoding:         outputType,
 		Development:      true,
 		OutputPaths:      []string{"stdout"},
@@ -210,14 +229,23 @@
 //instead of using the publicly available functions in this log package then a number of functionalities will not
 // be available to it, notably log tracing with filename.functionname.linenumber annotation.
 //
-func AddPackage(outputType string, level int, defaultFields Fields) (Logger, error) {
+// pkgNames parameter should be used for testing only as this function detects the caller's package.
+func AddPackage(outputType string, level int, defaultFields Fields, pkgNames ...string) (Logger, error) {
 	if cfgs == nil {
 		cfgs = make(map[string]zp.Config)
 	}
 	if loggers == nil {
 		loggers = make(map[string]*logger)
 	}
-	pkgName, _, _, _ := getCallerInfo()
+
+	var pkgName string
+	for _, name := range pkgNames {
+		pkgName = name
+		break
+	}
+	if pkgName == "" {
+		pkgName, _, _, _ = getCallerInfo()
+	}
 
 	if _, exist := loggers[pkgName]; exist {
 		return loggers[pkgName], nil
@@ -294,60 +322,7 @@
 	return loggers[pkgName], nil
 }
 
-//SetPackageLogLevel dynamically sets the log level of a given package to level.  This is typically invoked at an
-// application level during debugging
-func SetPackageLogLevel(packageName string, level int) {
-	// Get proper config
-	if cfg, ok := cfgs[packageName]; ok {
-		switch level {
-		case DebugLevel:
-			cfg.Level.SetLevel(zc.DebugLevel)
-		case InfoLevel:
-			cfg.Level.SetLevel(zc.InfoLevel)
-		case WarnLevel:
-			cfg.Level.SetLevel(zc.WarnLevel)
-		case ErrorLevel:
-			cfg.Level.SetLevel(zc.ErrorLevel)
-		case PanicLevel:
-			cfg.Level.SetLevel(zc.PanicLevel)
-		case FatalLevel:
-			cfg.Level.SetLevel(zc.FatalLevel)
-		default:
-			cfg.Level.SetLevel(zc.ErrorLevel)
-		}
-	}
-}
-
-//SetAllLogLevel sets the log level of all registered packages to level
-func SetAllLogLevel(level int) {
-	// Get proper config
-	for _, cfg := range cfgs {
-		switch level {
-		case DebugLevel:
-			cfg.Level.SetLevel(zc.DebugLevel)
-		case InfoLevel:
-			cfg.Level.SetLevel(zc.InfoLevel)
-		case WarnLevel:
-			cfg.Level.SetLevel(zc.WarnLevel)
-		case ErrorLevel:
-			cfg.Level.SetLevel(zc.ErrorLevel)
-		case PanicLevel:
-			cfg.Level.SetLevel(zc.PanicLevel)
-		case FatalLevel:
-			cfg.Level.SetLevel(zc.FatalLevel)
-		default:
-			cfg.Level.SetLevel(zc.ErrorLevel)
-		}
-	}
-}
-
-//SetLogLevel sets the log level for the logger corresponding to the caller's package
-func SetLogLevel(level int) error {
-	pkgName, _, _, _ := getCallerInfo()
-	if _, exist := cfgs[pkgName]; !exist {
-		return errors.New(fmt.Sprint("unregistered-package-%s", pkgName))
-	}
-	cfg := cfgs[pkgName]
+func setLevel(cfg zp.Config, level int) {
 	switch level {
 	case DebugLevel:
 		cfg.Level.SetLevel(zc.DebugLevel)
@@ -364,6 +339,41 @@
 	default:
 		cfg.Level.SetLevel(zc.ErrorLevel)
 	}
+}
+
+//SetPackageLogLevel dynamically sets the log level of a given package to level.  This is typically invoked at an
+// application level during debugging
+func SetPackageLogLevel(packageName string, level int) {
+	// Get proper config
+	if cfg, ok := cfgs[packageName]; ok {
+		setLevel(cfg, level)
+	}
+}
+
+//SetAllLogLevel sets the log level of all registered packages to level
+func SetAllLogLevel(level int) {
+	// Get proper config
+	for _, cfg := range cfgs {
+		setLevel(cfg, level)
+	}
+}
+
+//GetPackageLogLevel returns the current log level of a package.
+func GetPackageLogLevel(packageName string) (int, error) {
+	if cfg, ok := cfgs[packageName]; ok {
+		return levelToInt(cfg.Level.Level()), nil
+	}
+	return 0, errors.New(fmt.Sprintf("unknown-package-%s", packageName))
+}
+
+//SetLogLevel sets the log level for the logger corresponding to the caller's package
+func SetLogLevel(level int) error {
+	pkgName, _, _, _ := getCallerInfo()
+	if _, exist := cfgs[pkgName]; !exist {
+		return errors.New(fmt.Sprintf("unregistered-package-%s", pkgName))
+	}
+	cfg := cfgs[pkgName]
+	setLevel(cfg, level)
 	return nil
 }
 
@@ -593,7 +603,7 @@
 
 // V reports whether verbosity level l is at least the requested verbose level.
 func (l logger) V(level int) bool {
-	return l.parent.Core().Enabled(parseLevel(level))
+	return l.parent.Core().Enabled(intToLevel(level))
 }
 
 // With returns a logger initialized with the key-value pairs
diff --git a/common/log/log_test.go b/common/log/log_test.go
index 68d6ba3..88794b2 100644
--- a/common/log/log_test.go
+++ b/common/log/log_test.go
@@ -16,7 +16,6 @@
 package log
 
 import (
-	"github.com/opencord/voltha-go/common/log"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/grpclog"
 	"testing"
@@ -26,17 +25,17 @@
 Prerequite:  Start the kafka/zookeeper containers.
 */
 
-var testLogger log.Logger
+var testLogger Logger
 
 func TestInit(t *testing.T) {
 	var err error
-	testLogger, err = log.AddPackage(log.JSON, log.ErrorLevel, nil)
+	testLogger, err = AddPackage(JSON, ErrorLevel, nil)
 	assert.NotNil(t, testLogger)
 	assert.Nil(t, err)
 }
 
 func verifyLogLevel(t *testing.T, minimumLevel int) {
-	log.SetAllLogLevel(minimumLevel)
+	SetAllLogLevel(minimumLevel)
 	var success bool
 	for i := 0; i < 6; i++ {
 		success = testLogger.V(i)
@@ -60,19 +59,47 @@
 }
 
 func TestUpdateAllLoggers(t *testing.T) {
-	err := log.UpdateAllLoggers(log.Fields{"update": "update"})
+	err := UpdateAllLoggers(Fields{"update": "update"})
 	assert.Nil(t, err)
 }
 
 func TestUpdateLoggers(t *testing.T) {
-	testLogger, err := log.UpdateLogger(log.Fields{"update": "update"})
+	testLogger, err := UpdateLogger(Fields{"update": "update"})
 	assert.Nil(t, err)
 	assert.NotNil(t, testLogger)
 }
 
 func TestUseAsGrpcLoggerV2(t *testing.T) {
 	var grpcLogger grpclog.LoggerV2
-	thisLogger, _ := log.AddPackage(log.JSON, log.ErrorLevel, nil)
+	thisLogger, _ := AddPackage(JSON, ErrorLevel, nil)
 	grpcLogger = thisLogger
 	assert.NotNil(t, grpcLogger)
 }
+
+func TestUpdateLogLevel(t *testing.T) {
+	//	Let's create a bunch of logger each with a separate package
+	myLoggers := make(map[string]Logger)
+	pkgNames := []string{"/rw_core/core", "/db/model", "/kafka"}
+	for _, name := range pkgNames {
+		myLoggers[name], _ = AddPackage(JSON, ErrorLevel, nil, []string{name}...)
+	}
+	//Test updates to log levels
+	levels := []int{0, 1, 2, 3, 4, 5}
+	for _, expectedLevel := range levels {
+		for _, name := range pkgNames {
+			SetPackageLogLevel(name, expectedLevel)
+			l, err := GetPackageLogLevel(name)
+			assert.Nil(t, err)
+			assert.Equal(t, l, expectedLevel)
+		}
+	}
+	//Test set all package level
+	for _, expectedLevel := range levels {
+		SetAllLogLevel(expectedLevel)
+		for _, name := range pkgNames {
+			l, err := GetPackageLogLevel(name)
+			assert.Nil(t, err)
+			assert.Equal(t, l, expectedLevel)
+		}
+	}
+}
diff --git a/python/cli/main.py b/python/cli/main.py
index 00ea702..0fc9b5b 100755
--- a/python/cli/main.py
+++ b/python/cli/main.py
@@ -156,6 +156,41 @@
         while self.history:
             self.history.pop()
 
+    @options([
+        make_option('-p', '--package', action="store", dest='package',
+                    help="Package Name"),
+        make_option('-l', '--level', action='store', dest='level'),
+    ])
+    def do_log(self, line, opts):
+
+        def logLevel(level):
+            switcher= {
+                "DEBUG": 0,
+                "INFO":1,
+                "WARNING":2,
+                "ERROR":3,
+                "CRITICAL":4,
+                "FATAL":5
+            }
+            return switcher.get(level, 3)
+
+        if opts.level is None:
+            return
+
+        stub = self.get_stub()
+        kw = dict()
+        if opts.package:
+            kw['package_name'] = "github.com/opencord/voltha-go/" + opts.package
+
+        kw['level'] = logLevel(opts.level.upper())
+
+        try:
+            logging = voltha_pb2.Logging(**kw)
+            stub.UpdateLogLevel(logging)
+            self.poutput('success')
+        except Exception as e:
+            self.poutput('Exception - {})'.format(e))
+
     def do_launch(self, line):
         """If Voltha is not running yet, launch it"""
         raise NotImplementedError('not implemented yet')
@@ -267,21 +302,6 @@
                            if d.startswith(text)]
         return completions
 
-    def do_xpon(self, line):
-        """xpon <optional> [device_ID] - Enter xpon level command mode"""
-        device_id = line.strip()
-        if device_id:
-            stub = self.get_stub()
-            try:
-                res = stub.GetDevice(voltha_pb2.ID(id=device_id))
-            except Exception:
-                self.poutput(
-                    self.colorize('Error: ', 'red') + 'No device id ' +
-                    self.colorize(device_id, 'blue') + ' is found')
-                return
-        sub = XponCli(self.get_channel, device_id)
-        sub.cmdloop()
-
     def do_omci(self, line):
         """omci <device_ID> - Enter OMCI level command mode"""
 
diff --git a/rw_core/core/grpc_nbi_api_handler.go b/rw_core/core/grpc_nbi_api_handler.go
index 0cb9a14..d11c420 100644
--- a/rw_core/core/grpc_nbi_api_handler.go
+++ b/rw_core/core/grpc_nbi_api_handler.go
@@ -128,9 +128,13 @@
 }
 
 func (handler *APIHandler) UpdateLogLevel(ctx context.Context, logging *voltha.Logging) (*empty.Empty, error) {
-	log.Debugw("UpdateLogLevel-request", log.Fields{"newloglevel": logging.Level, "intval": int(logging.Level)})
+	log.Debugw("UpdateLogLevel-request", log.Fields{"package": logging.PackageName, "intval": int(logging.Level)})
 	out := new(empty.Empty)
-	log.SetPackageLogLevel(logging.PackageName, int(logging.Level))
+	if logging.PackageName == "" {
+		log.SetAllLogLevel(int(logging.Level))
+	} else {
+		log.SetPackageLogLevel(logging.PackageName, int(logging.Level))
+	}
 	return out, nil
 }