Index: /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/djproject/urls.py
===================================================================
--- /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/djproject/urls.py	(revision 2749)
+++ /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/djproject/urls.py	(working copy)
@@ -16,6 +16,7 @@
 from hive.report.generate_report import handle_report_generation
 from hive.controller.backup_controller import handle_backup_req
 from hive.controller.restore_controller import handle_restore_req
+from hive.controller.snmpv3_controller import handle_snmpv3_req
 
 js_info_dict = {
     #'packages': ('your.app.package',),
@@ -67,6 +68,7 @@
     url(r'^log/(?P<app>\w+)$', handle_log_location_app),
     url(r'^backup/(?P<path>.*)$', handle_backup_req),
     url(r'^restore/(?P<path>.*)$', handle_restore_req),
+    url(r'^snmpv3/(?P<path>.*)$', handle_snmpv3_req),
     url(r'^real_service$', real_service),
     url(r'^rs_block$', rs_block),
     url(r'^rs_block_import$', rs_block_import),
Index: /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/controller/snmpv3_controller.py
===================================================================
--- /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/controller/snmpv3_controller.py	(nonexistent)
+++ /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/controller/snmpv3_controller.py	(working copy)
@@ -0,0 +1,171 @@
+# -*- coding: utf-8 -*-
+import json
+from hive.util.utils import json_response
+from hive.services.snmpv3_service import (
+    save_snmpv3_device,
+    configure_telegraf_snmp,
+    get_snmpv3_device_config,
+    delete_snmpv3_device,
+    update_snmpv3_device_status,
+)
+from cm.lib.libbasic_operation import oper_log
+from hive.utils import andebug
+VALID_DEVICE_TYPES = ["apv", "vapv", "ag", "vxag", "asf", "vasf"]
+
+
+def handle_snmpv3_req(request, path=None):
+    """
+    Main SNMPv3 handler for all SNMPv3-related routes.
+    Supported endpoints:
+      POST   /snmpv3/config   → Configure or update SNMPv3 device
+      GET    /snmpv3/config   → Get SNMPv3 config(s)
+      DELETE /snmpv3/config   → Delete SNMPv3 device config
+      POST   /snmpv3/toggle   → Enable or disable SNMPv3 polling
+    """
+    try:
+        if request.method == 'POST':
+            if path == 'config':
+                return configure_snmpv3_device(request)
+            elif path == 'toggle':
+                return toggle_snmpv3_device(request)
+
+        elif request.method == 'GET' and path == 'config':
+            return get_snmpv3_config(request)
+
+        elif request.method == 'DELETE' and path == 'config':
+            return delete_snmpv3_device_entry(request)
+
+        return json_response({
+            'error': 400,
+            'message': "Invalid HTTP method or path"
+        }, status=400)
+
+    except Exception as e:
+        oper_log('error', 'system', "SNMPv3 handler exception: %s" % str(e))
+        return json_response({
+            'error': 500,
+            'message': "Internal error while processing SNMPv3 request"
+        }, status=500)
+
+
+def configure_snmpv3_device(request):
+    """
+    Configure SNMPv3 for a device (save + reconfigure telegraf).
+
+    Accepts sec_level = "authPriv" or "authNoPriv".
+    If not provided, defaults to "authPriv".
+    """
+    try:
+        data = json.loads(request.body)
+    except Exception:
+        return json_response({"error": "Invalid JSON body"}, status=400)
+
+    required = ["device_ip", "username", "auth_pass", "auth_protocol", "device_type"]
+    # priv_pass and priv_protocol required only for authPriv
+    sec_level = data.get("sec_level", "authPriv")
+
+    if sec_level not in ["authPriv", "authNoPriv"]:
+        return json_response({"error": "Invalid sec_level (must be 'authPriv' or 'authNoPriv')"}, status=400)
+
+    if sec_level == "authPriv":
+        required += ["priv_pass", "priv_protocol"]
+
+    for field in required:
+        if field not in data or not data[field]:
+            return json_response({"error": "Missing field: %s" % field}, status=400)
+
+    # Normalize
+    data["device_type"] = data["device_type"].strip().lower()
+    data["sec_level"] = sec_level
+
+    if data["device_type"] not in VALID_DEVICE_TYPES:
+        return json_response({"error": "Invalid device_type"}, status=400)
+
+    try:
+        # Step 1: Save SNMPv3 device config
+        success, msg = save_snmpv3_device(data)
+        if not success:
+            oper_log('error', 'system', "SNMPv3 device save failed: %s" % msg)
+            return json_response({"error": msg}, status=500)
+
+        # Step 2: Regenerate Telegraf config
+        success, msg = configure_telegraf_snmp()
+        if not success:
+            oper_log('error', 'system', "Telegraf config update failed: %s" % msg)
+            return json_response({"error": msg}, status=500)
+
+        oper_log('info', 'system', "SNMPv3 device configured successfully")
+        return json_response({"message": "SNMPv3 configuration successful"}, status=200)
+
+    except Exception as e:
+        oper_log('error', 'system', "SNMPv3 configuration error: %s" % str(e))
+        return json_response({
+            "error": "Internal error while configuring SNMPv3"
+        }, status=500)
+
+
+def toggle_snmpv3_device(request):
+    """Enable or disable SNMPv3 polling for a device."""
+    try:
+        try:
+            data = json.loads(request.body)
+        except Exception as e:
+            return json_response({"error": "Invalid JSON payload"}, status=400)
+
+        device_ip = data.get("device_ip")
+        enabled = data.get("enabled")
+
+        if not device_ip or enabled is None:
+            return json_response({"error": "Missing required fields: device_ip and enabled"}, status=400)
+
+        success, msg = update_snmpv3_device_status(device_ip, enabled)
+        if not success:
+            oper_log('error', 'system', "SNMPv3 toggle failed: %s" % msg)
+            return json_response({"error": "Failed to update device status"}, status=500)
+
+        oper_log('info', 'system', "SNMPv3 device %s enabled=%s" % (device_ip, enabled))
+        return json_response({"message": "Device polling status updated successfully"}, status=200)
+
+    except Exception as e:
+        oper_log('error', 'system', "SNMPv3 toggle error: %s" % str(e))
+        return json_response({
+            "error": "Internal error while toggling SNMPv3 device"
+        }, status=500)
+
+
+def get_snmpv3_config(request):
+    """Get SNMPv3 configuration for a specific device or all devices."""
+    try:
+        device_ip = request.GET.get('device_ip')
+        success, data = get_snmpv3_device_config(device_ip)
+
+        if success:
+            return json_response(data, status=200)
+        return json_response({"error": data}, status=404)
+
+    except Exception as e:
+        oper_log('error', 'system', "Failed to fetch SNMPv3 config: %s" % str(e))
+        return json_response({
+            "error": "Internal error while fetching SNMPv3 configuration"
+        }, status=500)
+
+
+def delete_snmpv3_device_entry(request):
+    """Delete SNMPv3 configuration for a device."""
+    try:
+        device_ip = request.GET.get('device_ip')
+        if not device_ip:
+            return json_response({"error": "Device IP required"}, status=400)
+
+        success, msg = delete_snmpv3_device(device_ip)
+        if success:
+            oper_log('info', 'system', "Deleted SNMPv3 config for %s" % device_ip)
+            return json_response({"message": msg}, status=200)
+
+        return json_response({"error": msg}, status=500)
+
+    except Exception as e:
+        oper_log('error', 'system', "Failed to delete SNMPv3 device: %s" % str(e))
+        return json_response({
+            "error": "Internal error while deleting SNMPv3 configuration"
+        }, status=500)
Index: /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/services/snmpv3_service.py
===================================================================
--- /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/services/snmpv3_service.py	(nonexistent)
+++ /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/services/snmpv3_service.py	(working copy)
@@ -0,0 +1,358 @@
+# -*- coding: utf-8 -*-
+import os
+import json
+import time
+import subprocess
+from cm.lib.libbasic_operation import oper_log
+from hive.utils import andebug
+
+COMPOSER_TELEGRAF_CONF = "/usr/local/etc/composer/composer_tele.conf"
+SNMPV3_DEVICES_PATH = "/var/lib/snmp/snmpv3_devices.json"
+SNMP_TOML_DIR = "/ca/webui/conf/snmp"
+
+
+def _load_json(path):
+    """Helper to safely load JSON."""
+    if not os.path.exists(path):
+        return []
+    try:
+        with open(path, "r") as f:
+            return json.load(f)
+    except ValueError:
+        return []
+
+
+def _save_json(path, data):
+    """Helper to safely save JSON."""
+    with open(path, "w") as f:
+        json.dump(data, f, indent=4)
+
+
+def save_snmpv3_device(data):
+    """Save or update SNMPv3 device configuration."""
+    try:
+        # Ensure 'enabled' flag exists — default True
+        if "enabled" not in data:
+            data["enabled"] = True
+
+        devices = _load_json(SNMPV3_DEVICES_PATH)
+        updated = False
+
+        # Update existing entry if found
+        for i, d in enumerate(devices):
+            if d.get("device_ip") == data["device_ip"]:
+                devices[i] = data
+                updated = True
+                break
+
+        # Otherwise append new device
+        if not updated:
+            devices.append(data)
+
+        # Ensure directory exists
+        directory = os.path.dirname(SNMPV3_DEVICES_PATH)
+        if not os.path.exists(directory):
+            os.makedirs(directory)
+
+        # Save back to JSON
+        _save_json(SNMPV3_DEVICES_PATH, devices)
+        oper_log('info', 'system', "SNMPv3 device saved: %s" % data["device_ip"])
+        return True, "SNMPv3 device saved successfully"
+
+    except Exception as e:
+        oper_log('error', 'system', "Failed to save SNMPv3 device: %s" % str(e))
+        return False, str(e)
+
+
+def delete_snmpv3_device(device_ip):
+    """Remove a device entry and update Telegraf config."""
+    try:
+        devices = _load_json(SNMPV3_DEVICES_PATH)
+        new_devices = [d for d in devices if d.get("device_ip") != device_ip]
+
+        if len(new_devices) == len(devices):
+            return False, "Device not found in SNMPv3 configuration."
+
+        _save_json(SNMPV3_DEVICES_PATH, new_devices)
+        oper_log('info', 'system', "Removed SNMPv3 device: %s" % device_ip)
+
+        # Rebuild Telegraf config after removal
+        configure_telegraf_snmp()
+        return True, "SNMPv3 device removed and Telegraf updated."
+    except Exception as e:
+        oper_log('error', 'system', "Failed to remove SNMPv3 device: %s" % str(e))
+        return False, str(e)
+
+
+def get_snmpv3_device_config(device_ip=None):
+    """
+    Return SNMPv3 configuration for a specific device or all devices.
+    """
+    try:
+        devices = _load_json(SNMPV3_DEVICES_PATH)
+
+        if not devices:
+            return False, "No SNMPv3 devices found."
+
+        if device_ip:
+            for d in devices:
+                if d.get("device_ip") == device_ip:
+                    return True, d
+            return False, "No SNMPv3 configuration found for device %s" % device_ip
+
+        return True, devices
+    except Exception as e:
+        oper_log('error', 'system', "Failed to fetch SNMPv3 config: %s" % str(e))
+        return False, str(e)
+
+
+def _load_toml_fields(device_type):
+    """Lightweight TOML parser for SNMP OID files (no external dependencies).
+
+      Example TOML:
+        [[inputs.snmp.field]]
+        name = "cpu_usage"
+        oid = ".1.3.6.1.4.1.7564.30.1.0"
+
+        [[inputs.snmp.table]]
+        name = "realStats"
+
+        [[inputs.snmp.table.field]]
+        name = "rsConnCnt"
+        oid = ".1.3.6.1.4.1.7564.19.2.1.1.1.6"
+        is_tag = true
+
+    Returns:
+        tuple: (fields, tables)
+            fields → List of dicts like:
+                [
+                    {"name": "cpu_usage", "oid": ".1.3.6.1.4.1.7564.30.1.0"}
+                ]
+            tables → List of dicts like:
+                [
+                    {
+                        "name": "realStats",
+                        "field": [
+                            {"name": "rsConnCnt", "oid": ".1.3.6.1.4.1.7564.19.2.1.1.1.6", "is_tag": "true"}
+                        ]
+                    }
+                ]
+    """
+    toml_map = {
+        'apv': '/ca/webui/conf/snmp/apv.toml',
+        'vapv': '/ca/webui/conf/snmp/apv.toml',
+        'ag': '/ca/webui/conf/snmp/ag.toml',
+        'vxag': '/ca/webui/conf/snmp/ag.toml',
+        'asf': '/ca/webui/conf/snmp/asf.toml',
+        'vasf': '/ca/webui/conf/snmp/asf.toml'
+    }
+
+    file_path = toml_map.get(device_type.lower())
+    if not file_path or not os.path.exists(file_path):
+        return [], []
+
+    fields, tables = [], []
+    current_table = None
+    current_field = None
+
+    with open(file_path, 'r') as f:
+        for line in f:
+            line = line.strip()
+            if not line or line.startswith('#'):
+                continue
+
+            if line.startswith('[[inputs.snmp.field]]'):
+                current_table = None
+                current_field = {}
+                fields.append(current_field)
+                continue
+
+            if line.startswith('[[inputs.snmp.table]]'):
+                current_table = {'name': None, 'field': []}
+                tables.append(current_table)
+                continue
+
+            if line.startswith('[[inputs.snmp.table.field]]'):
+                if current_table is not None:
+                    current_field = {}
+                    current_table['field'].append(current_field)
+                continue
+
+            if '=' not in line:
+                continue
+
+            key, val = [x.strip() for x in line.split('=', 1)]
+            val = val.strip('"').strip("'")
+
+            if current_table is not None:
+                if key == 'name' and current_table.get('name') is None:
+                    current_table['name'] = val
+                elif current_field is not None:
+                    current_field[key] = val
+            elif current_field is not None:
+                current_field[key] = val
+
+    return fields, tables
+
+
+def configure_telegraf_snmp():
+    """Generate SNMPv3 Telegraf config based on stored devices (supports authPriv / authNoPriv)."""
+    andebug('an.model.cli', 'Inside configure_telegraf_snmp')
+    try:
+        devices = _load_json(SNMPV3_DEVICES_PATH)
+        andebug('an.model.cli', 'devices: {}'.format(devices))
+        if not devices:
+            return False, "No SNMPv3 devices defined"
+
+        lines = []
+
+        for d in devices:
+            if not d.get("enabled", True):
+                continue
+
+            device_ip = d.get("device_ip")
+            device_type = d.get("device_type", "").lower()
+            sec_level = d.get("sec_level", "authPriv")
+            if not device_ip:
+                continue
+
+            # Load SNMP OIDs dynamically from TOML
+            fields, tables = _load_toml_fields(device_type)
+
+            # --- SNMPv3 section header ---
+            lines.extend([
+                '[[inputs.snmp]]',
+                '  agents = ["%s:161"]' % device_ip,
+                '  version = 3',
+                '  name = "snmpv3_metrics_%s"' % device_ip.replace('.', '_'),
+                '  timeout = "2s"',
+                '  sec_name = "%s"' % d["username"],
+                '  auth_protocol = "%s"' % d["auth_protocol"],
+                '  auth_password = "%s"' % d["auth_pass"]
+            ])
+
+            # Optional encryption fields (only for authPriv)
+            if sec_level == "authPriv":
+                lines.extend([
+                    '  priv_protocol = "%s"' % d.get("priv_protocol", ""),
+                    '  priv_password = "%s"' % d.get("priv_pass", "")
+                ])
+
+            # Security level
+            lines.append('  sec_level = "%s"' % sec_level)
+            lines.append('')
+
+            # --- Add SNMP fields ---
+            for f in fields:
+                if not f.get("name") or not f.get("oid"):
+                    continue
+                lines.extend([
+                    '  [[inputs.snmp.field]]',
+                    '    name = "%s"' % f["name"],
+                    '    oid = "%s"' % f["oid"]
+                ])
+                if f.get("conversion"):
+                    lines.append('    conversion = "%s"' % f["conversion"])
+                if f.get("is_tag"):
+                    lines.append('    is_tag = true')
+                lines.append('')
+
+            # --- Add SNMP tables ---
+            for table in tables:
+                if not isinstance(table, dict):
+                    continue
+                tname = table.get("name")
+                if not tname:
+                    continue
+
+                lines.append('  [[inputs.snmp.table]]')
+                lines.append('    name = "%s"' % tname)
+
+                if "prefix" in table:
+                    lines.append('    prefix = %s' % table["prefix"])
+
+                fields_list = table.get("field", [])
+                for f in fields_list:
+                    if not f.get("name") or not f.get("oid"):
+                        continue
+                    lines.extend([
+                        '    [[inputs.snmp.table.field]]',
+                        '      name = "%s"' % f["name"],
+                        '      oid = "%s"' % f["oid"]
+                    ])
+                    if f.get("conversion"):
+                        lines.append('      conversion = "%s"' % f["conversion"])
+                    if f.get("is_tag"):
+                        lines.append('      is_tag = true')
+                    lines.append('')
+
+            lines.append('')  # spacing between devices
+
+        # --- Write new config ---
+        start_marker = "# BEGIN SNMPv3 AUTOGEN"
+        end_marker = "# END SNMPv3 AUTOGEN"
+
+        if os.path.exists(COMPOSER_TELEGRAF_CONF):
+            with open(COMPOSER_TELEGRAF_CONF, "r") as f:
+                existing = f.read()
+        else:
+            existing = "[agent]\ninterval = \"10s\"\n\n"
+
+        if start_marker in existing and end_marker in existing:
+            pre = existing.split(start_marker)[0]
+            post = existing.split(end_marker)[-1]
+            new_conf = pre + start_marker + "\n" + "\n".join(lines) + "\n" + end_marker + post
+        else:
+            new_conf = existing.strip() + "\n\n" + start_marker + "\n" + "\n".join(lines) + "\n" + end_marker + "\n"
+
+        with open(COMPOSER_TELEGRAF_CONF, "w") as f:
+            f.write(new_conf)
+
+        # --- Restart composer_tele safely ---
+        subprocess.call(["pkill", "-f", "composer_tele"])
+        time.sleep(2)
+        subprocess.Popen([
+            "/ca/extensions/auditing/syslogd/composer_tele",
+            "--config", COMPOSER_TELEGRAF_CONF
+        ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        time.sleep(5)
+        ps_check = subprocess.call(["pgrep", "-f", "composer_tele"])
+        if ps_check != 0:
+            oper_log('error', 'system', "composer_tele did not start properly")
+        else:
+            oper_log('info', 'system', "composer_tele restarted successfully")
+
+        oper_log('info', 'system', "Telegraf SNMPv3 configuration updated successfully")
+        return True, "Telegraf SNMPv3 configuration updated successfully"
+
+    except Exception as e:
+        oper_log('error', 'system', "Failed to configure Telegraf SNMPv3: %s" % str(e))
+        return False, str(e)
+
+
+def update_snmpv3_device_status(device_ip, enabled):
+    """Enable or disable SNMPv3 polling for a device."""
+    try:
+        devices = _load_json(SNMPV3_DEVICES_PATH)
+        found = False
+
+        for d in devices:
+            if d.get("device_ip") == device_ip:
+                d["enabled"] = bool(enabled)
+                found = True
+                break
+
+        if not found:
+            return False, "Device not found in SNMPv3 configuration."
+
+        _save_json(SNMPV3_DEVICES_PATH, devices)
+        oper_log('info', 'system', "Updated SNMPv3 device %s: enabled=%s" % (device_ip, enabled))
+
+        # Rebuild telegraf config after toggle
+        configure_telegraf_snmp()
+
+        return True, "Device polling status updated successfully."
+    except Exception as e:
+        oper_log('error', 'system', "Failed to update SNMPv3 device status: %s" % str(e))
+        return False, str(e)
Index: /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/util/utils.py
===================================================================
--- /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/util/utils.py	(nonexistent)
+++ /branches/amp_3_7_2/src/webui/webui/htdocs/new/src/hive/util/utils.py	(working copy)
@@ -0,0 +1,6 @@
+import json
+from django.http import HttpResponse
+
+
+def json_response(data, status=200):
+    return HttpResponse(json.dumps(data), content_type="application/json", status=status)
\ No newline at end of file
