Repository: sonic-net/sonic-utilities

Issue:
make ztp to have non-sensitive information only visible to non-root user
<!--
If you are reporting a new issue, make sure that we do not have any duplicates
already open. You can ensure this by searching the issue list for this
repository. If there is a duplicate, please close your issue and add a comment
to the existing issue instead.

If you suspect your issue is a bug, please edit your issue description to
include the BUG REPORT INFORMATION shown below. If you fail to provide this
information within 7 days, we cannot debug your issue and will close it. We
will, however, reopen it if you later provide the information.

For more information about reporting issues, see
https://github.com/Azure/SONiC/wiki#report-issues

---------------------------------------------------
GENERAL SUPPORT INFORMATION
---------------------------------------------------

The GitHub issue tracker is for bug reports and feature requests.
General support can be found at the following locations:

- SONiC Support Forums - https://groups.google.com/forum/#!forum/sonicproject

---------------------------------------------------
BUG REPORT INFORMATION
---------------------------------------------------
Use the commands below to provide key information from your environment:
You do NOT have to include this information if this is a FEATURE REQUEST
-->

**Description**
The ask is to limit the scope of privilege to only the sensitive material (e.g. URLs pointing to files containing user info), such that non-sensitive information (e.g. ZTP admin state) is visible to the non-root user. We understand the ask, and can look into this, but we would like you to consider allowing this to be a later enhancement. That is, let's approve/merge this PR, merge it to the 201911 release branch, and tackle this feedback in parallel. Is that OK?

<!--
Briefly describe the problem you are having in a few paragraphs.
-->

**Steps to reproduce the issue**
1.
2.
3.

**Describe the results you received**


**Describe the results you expected**


**Additional information you deem important (e.g. issue happens only occasionally)**


**Output of `show version`**

```
(paste your output here)
```

<!--
Also attach debug file produced by `sudo generate_dump`
-->




Discussion:
@rajendra-dendukuri

Resolving patch:
diff --git a/config/main.py b/config/main.py
--- a/config/main.py
+++ b/config/main.py
@@ -36,6 +36,11 @@
 SYSTEMCTL_ACTION_RESET_FAILED="reset-failed"
 
 DEFAULT_NAMESPACE = ''
+CFG_LOOPBACK_PREFIX = "Loopback"
+CFG_LOOPBACK_PREFIX_LEN = len(CFG_LOOPBACK_PREFIX)
+CFG_LOOPBACK_NAME_TOTAL_LEN_MAX = 11
+CFG_LOOPBACK_ID_MAX_VAL = 999
+CFG_LOOPBACK_NO="<0-999>"
 # ========================== Syslog wrappers ==========================
 
 def log_debug(msg):
@@ -2641,6 +2646,71 @@ def naming_mode_alias():
     """Set CLI interface naming mode to ALIAS (Vendor port alias)"""
     set_interface_naming_mode('alias')
 
+@config.group()
+def is_loopback_name_valid(loopback_name):
+    """Loopback name validation
+    """
+    
+    if loopback_name[:CFG_LOOPBACK_PREFIX_LEN] != CFG_LOOPBACK_PREFIX :
+        return False
+    if (loopback_name[CFG_LOOPBACK_PREFIX_LEN:].isdigit() is False or 
+          int(loopback_name[CFG_LOOPBACK_PREFIX_LEN:]) > CFG_LOOPBACK_ID_MAX_VAL) :
+        return False
+    if len(loopback_name) > CFG_LOOPBACK_NAME_TOTAL_LEN_MAX:
+        return False
+    return True
+
+#
+# 'loopback' group ('config loopback ...')
+#
+@config.group()
+@click.pass_context
+@click.option('-s', '--redis-unix-socket-path', help='unix socket path for redis connection')
+def loopback(ctx, redis_unix_socket_path):
+    """Loopback-related configuration tasks"""
+    kwargs = {}
+    if redis_unix_socket_path:
+        kwargs['unix_socket_path'] = redis_unix_socket_path
+    config_db = ConfigDBConnector(**kwargs)
+    config_db.connect(wait_for_init=False)
+    ctx.obj = {'db': config_db}
+
+@loopback.command('add')
+@click.argument('loopback_name', metavar='<loopback_name>', required=True)
+@click.pass_context
+def add_loopback(ctx, loopback_name):
+    config_db = ctx.obj['db']
+    if is_loopback_name_valid(loopback_name) is False:
+        ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' "
+                .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO))
+
+    lo_intfs = [k for k,v in config_db.get_table('LOOPBACK_INTERFACE').iteritems() if type(k) != tuple]
+    if loopback_name in lo_intfs:
+        ctx.fail("{} already exists".format(loopback_name))
+
+    config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, {"NULL" : "NULL"})
+
+@loopback.command('del')
+@click.argument('loopback_name', metavar='<loopback_name>', required=True)
+@click.pass_context
+def del_loopback(ctx, loopback_name):
+    config_db = ctx.obj['db']
+    if is_loopback_name_valid(loopback_name) is False:
+        ctx.fail("{} is invalid, name should have prefix '{}' and suffix '{}' "
+                .format(loopback_name, CFG_LOOPBACK_PREFIX, CFG_LOOPBACK_NO))
+
+    lo_config_db = config_db.get_table('LOOPBACK_INTERFACE')
+    lo_intfs = [k for k,v in lo_config_db.iteritems() if type(k) != tuple]
+    if loopback_name not in lo_intfs:
+        ctx.fail("{} does not exists".format(loopback_name))
+
+    ips = [ k[1] for k in lo_config_db if type(k) == tuple and k[0] == loopback_name ]
+    for ip in ips:
+        config_db.set_entry('LOOPBACK_INTERFACE', (loopback_name, ip), None)
+
+    config_db.set_entry('LOOPBACK_INTERFACE', loopback_name, None)
+
+
 @config.group(cls=AbbreviationGroup)
 def ztp():
     """ Configure Zero Touch Provisioning """


Test patch: