An application for Network engineers to manage Cisco devices (Python Tkinter).
import sqlite3
from mysql.connector import Error
import customtkinter as ctk
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from tkinter import *
from PIL import ImageTk, Image
from netmiko import ConnectHandler
import time
import os
import shutil
from settings import *
from connecttodb import getdevicefromid
import regex as re
from netmiko.exceptions import NetMikoAuthenticationException
from netmiko.exceptions import NetMikoTimeoutException
from netmiko.exceptions import SSHException
# from netmiko.ssh_exception import NetMikoAuthenticationException
# from netmiko.ssh_exception import NetMikoTimeoutException
# from netmiko.ssh_exception import SSHException

def tftpbtngo(self, selectedid):
    
    # Disable the button from multiple presses
    # put a feeedback here
    self.btnbackup.configure(state='disabled', fg_color="gray", border_color='gray')
    # self.tftpbackupwindow, text="Backup", 
    self.progstatus.set(0.5)
    self.editgrpstatuslbl.configure(text=f"Starting process on id: {selectedid}")
    self.update_idletasks()

    # Disable Backup type field
    # self.bktpeselectlbl.configure(state='disabled')
    # self.bktpeselecchkbx.configure(fg_color="gray75", state='disabled', border_color='gray', button_color='gray')
    # self.bktpeselecbtn.configure(text='Accepted', fg_color="gray", state='disabled')
    # Disable File Identifier
    # self.fileidlbl.configure(state='disabled') # label
    # self.fileidselec.configure(fg_color="gray75", state='disabled', border_color='gray', button_color='gray')
    # self.fileidbtn.configure(text='Accepted', fg_color="gray", state='disabled')

    self.cgouttxtbx.configure(state='normal')
    # self.cgouttxtbx.insert(INSERT, f"id: {selectedid}\n")

    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retdescription=""
    rettype=""
    retgolden=""

    retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(selectedid)
    
    print("1) This is tftpbtngo(backuptftp.py)")
    print(f"2) This is tftpbtngo (iam_selecteddevice): {str(selectedid)}")
    print(f"3) This is tftpbtngo (id): {str(retid)}")
    print(f"4) This is tftpbtngo (name): {retname}")
    print(f"5) This is tftpbtngo (ip): {retip}")
    print(f"6) This is tftpbtngo (username): {retusername}")
    print(f"7) This is tftpbtngo (password): {retpassword}")
    print(f"8) This is tftpbtngo (retdescription): {retdescription}")
    print(f"9) This is tftpbtngo (rettype): {rettype}")
    print(f"10) This is tftpbtngo (retgolden): {retgolden}")
    
    hostname = ""
    filename = ""
    filedatetime = str(time.strftime("%Y-%m-%d-%H-%M-%S"))
    hostname = retname
    # filedestination = f"C:\\Shared\\NetDevApp3\\cisco12ct\\devicedata\\{hostname}\\" # windows
    # filedestination = f'/Shared/NetDevApp3/cisco12ct/devicedata/' # Linux
    
    # enter the IP for your TFTP server here
    TFTP_SERVER = tftpserver
    print(f"The TFTP_SERVER is: {TFTP_SERVER}")
    print(f"The directorylocation is: {directorylocation}")
    print(f"The filesource is: {filesource}")
    print(f"The filedestination is: {filedestination}")
    print(f"The changetoDEVICEDATAdirectory is: {changetoDEVICEDATAdirectory}")
    
    # cisco_ios
    # cisco_xe
    device = {
        'device_type': f'{rettype}',
        'host': f'{retip}',
        'username': f'{retusername}',
        'password': f'{retpassword}',
        'secret': f'{retpassword}'
        }
    print("--------------")
    print(device['host']) # ip address
    print(device['device_type']) # device type
    print(f"1) self.fileidselec.get(): {self.fileidselec.get()}") # Daily
    print(f"2) self.bktpeselecchkbx.get(): {self.bktpeselecchkbx.get()}") # Running-configuration
    print(f"3) self.fileidselec.get(): {self.fileidselec.get()}") # Daily
    print(f"4) self.tftpselectedid.get(): {self.tftpselectedid.get()}") # 2
    print(f"5) self.setbackuptypevar.get(): {self.setbackuptypevar.get()}") # Golden
    print(f"6) self.setfileidtypevar.get(): {self.setfileidtypevar.get()}")
    self.cgouttxtbx.insert(INSERT, f"1) connecting to device id:{selectedid}"+" "+f"{device['host']}"+"\n")
    print("--------------")

    # put a feeedback here
    self.progstatus.set(0.6)
    self.editgrpstatuslbl.configure(text=f"Backup process running")
    self.update_idletasks()

    try:
        name = f"device{str(1)}"
        net_connect = ConnectHandler(**device)
        print("Try 0a")
        net_connect.enable()
        print(net_connect.find_prompt()[:-1])
        hostname = net_connect.find_prompt()[:-1]
        #copy_command = f"copy running-config tftp://{tftpserver}{directorylocation}"
        copy_command = f"copy {self.setbackuptypevar.get()} tftp://{tftpserver}"
        output = net_connect.send_command_timing(copy_command)
        print(f"Try 1 hostname={hostname}")
        self.cgouttxtbx.insert(INSERT, f"2) connection hostname from device is :{hostname}"+"\n")
        
        # put a feeedback here
        self.progstatus.set(0.7)
        self.editgrpstatuslbl.configure(text=f"Hostname found: {hostname}")
        self.update_idletasks()
        
        
        if "Address or name" in output:
            output += net_connect.send_command_timing("\n")
            print("Try 2")
            self.cgouttxtbx.insert(INSERT, f"3) Accepting the tftp server ip address: {tftpserver}"+"\n")
        if "Destination filename" in output:
            ####### YOU WILL NEED TO HARDCODE THIS ? 
            ################################################################################
            print("backupftp.py tftpbtngo Line:112 Select Windows(111) or Linux(108)")
            #use this for Linux
            output += net_connect.send_command_timing(f"{directorylocation}"+"/"+f"{hostname}_"+f"{filedatetime}_"+f"{self.setbackuptypevar.get()}"+f"_{self.setfileidtypevar.get()}"+"\n")
            self.cgouttxtbx.insert(INSERT, f"4) The TFTP temporary save directory will be: {directorylocation}"+"\n")
            self.cgouttxtbx.insert(INSERT, f"5) The filename will be:\n{hostname}_"+f"{filedatetime}_"+f"{self.setbackuptypevar.get()}"+f"_{self.setfileidtypevar.get()}"+"\n")
            
            # Use this for windows
            # output += net_connect.send_command_timing(f"{hostname}_"+f"{filedatetime}_"+f"{self.setbackuptypevar.get()}"+f"_{self.setfileidtypevar.get()}"+"\n")
            # self.cgouttxtbx.insert(INSERT, f"3) The filename will be: {hostname}_"+f"{filedatetime}_"+f"{self.setbackuptypevar.get()}"+f"_{self.setfileidtypevar.get()}"+"\n")
            ##################################################################################
            
            net_connect.disconnect

    except NetMikoAuthenticationException as AuthException:
        print("151 Authentication Error %s" % AuthException)
        self.cgouttxtbx.insert(INSERT, f"***--- {hostname} ({retip}) ---*** \n Authentication Error \n {AuthException}")
    except NetMikoTimeoutException as TimeoutException:
        #raise ConnectError(TimeoutException.args[0])
        print("155 No Valid Connections Error SSH connection 'USERNAME ?': %s" % TimeoutException)
        self.cgouttxtbx.insert(INSERT, f"Timeout Exception Error ***--- {hostname} ({retip}) ---*** \n {TimeoutException}")
    except SSHException as sshexception:
        print("158 An netmiko sshexception Error %s" % sshexception)
        self.cgouttxtbx.insert(INSERT, f"SSHException Error ***--- {hostname} ({retip}) ---*** \n {sshexception}")
    except Exception as unknown_error:
        print("161 An netmiko Unknown Error %s"+ str(unknown_error))
        self.cgouttxtbx.insert(INSERT, f"Unknown Error ***--- {hostname} ({retip}) ---*** \n  \n {unknown_error}")
    except:
        print("164 A netmiko function exception occurred in tftpconfig(backuptftp.py)")
        self.cgouttxtbx.insert(INSERT, f"6) A netmiko connection exception occurred: {device['host']}\n")
    
    finally:
        try:
            print(type(output))
            self.cgouttxtbx.insert(INSERT, f"6) File was sucessfully transfered: {output[output.rindex('!')+1:]}\n")
        except:
            print("Could not print the output of the netmiko function tftpconfig(backuptftp.py)")
            self.cgouttxtbx.insert(INSERT, f"6) Could not print the output\n")
    
    print("Running the second try method")
    print(f"Directory = {directorylocation}")
    print(os.listdir(directorylocation))
    # print("ScanDir = "+ os.scandir(directorylocation))
    print(f"hostname={hostname}")
    print(f"device ip={device['host']}")
    print(f"{retname}")
    print("CURRENT DIRECTORY="+f"{os.getcwd()}")
    os.chdir(changetoDEVICEDATAdirectory)
    print("CURRENT DIRECTORY2="+f"{os.getcwd()}")

    # self.cgouttxtbx.insert(INSERT, f"CURRENT DIRECTORY2: {os.getcwd()}")

    # self.cgouttxtbx.configure(state='disabled')


    if hostname == retname:
        print("Both names match !")
        try:
            for file_name in os.listdir(filesource):
                source=filesource+file_name
                destination=filedestination+"/"+hostname+"/"+file_name
                if os.path.isfile(source):
                    shutil.copy(source, destination)
        except OSError as error:
            print("Problem copying files to location = " + f"{error}")
            print("source = " + f"{source}")
            print("filesource = " + f"{filesource}")
            print("file_name = " + f"{file_name}")
            print("destination = " + f"{destination}")
            print("filedestination = " + f"{filedestination}")
            print("hostname = " + f"{hostname}")

        finally:
            if len(os.listdir(filesource)) > 0:
                print("FOLDER IS GREATER THAN O")
                # shutil.rmtree(filesource)
                for f in os.listdir(filesource):
                    os.remove(os.path.join(filesource, f))
            else:
                print("FOLDER IS NOT GREATER THAN 0")

    else:
        print("Both names DONOT match !")
    
    # put a feeedback here
    self.progstatus.set(1)
    self.editgrpstatuslbl.configure(text=f"Process complete")
    self.cgouttxtbx.insert(INSERT, f"7) Backup process complete")
    self.cgouttxtbx.configure(state='disabled')


# THIS FUNCTION IS NOW REDUNDANT ooooohh no its not
'''
def tftpsaveconfig1(self, iam_selecteddevice):

    self.progstatus.set(0.4)
    self.editgrpstatuslbl.configure(text=f"Config process on id:{iam_selecteddevice}")
    self.update_idletasks()

    # Get iam_selecteddevice values (connecttodb.py > getdevicefromid(privide id))
    # return retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden
    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retdescription=""
    rettype=""
    retgolden=""

    retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(iam_selecteddevice)
    
    print("1) This is tftpconfig(backuptftp.py)")
    print(f"2) This is tftpsaveconfig (iam_selecteddevice): {str(iam_selecteddevice)}")
    print(f"3) This is tftpsaveconfig (id): {str(retid)}")
    print(f"4) This is tftpsaveconfig (name): {retname}")
    print(f"5) This is tftpsaveconfig (ip): {retip}")
    print(f"6) This is tftpsaveconfig (username): {retusername}")
    print(f"7) This is tftpsaveconfig (password): {retpassword}")
    print(f"8) This is tftpsaveconfig (retdescription): {retdescription}")
    print(f"9) This is tftpsaveconfig (rettype): {rettype}")
    print(f"10) This is tftpsaveconfig (retgolden): {retgolden}")
    print(f"The tftpserver is: {tftpserver}")

    # enter the IP for your TFTP server here
    
    TFTP_SERVER = tftpserver
    print(f"The TFTP_SERVER is: {TFTP_SERVER}")
    
    hostname = ""
    filename = ""
    
    directorylocation = "/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/"
    filesource = '/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/'
    filedestination = f'/Shared/NetDevApp3/cisco12ct/devicedata/{hostname}/'
    changetoDEVICEDATAdirectory = f'/Shared/NetDevApp3/cisco12ct'
    
    filedatetime = str(time.strftime("%Y-%m-%d-%H-%M-%S"))
    
    device = {
    'device_type': 'cisco_ios',
    'host': f'{retip}',
    'username': f'{retusername}',
    'password': f'{retpassword}',
    'secret': f'{retpassword}'
    }
    print("--------------")
    print(device['host'])
    print("--------------")
    try:
        name = f"device{str(1)}"
        net_connect = ConnectHandler(**device)
        print("Try 0a")
        net_connect.enable()
        print(net_connect.find_prompt()[:-1])
        hostname = net_connect.find_prompt()[:-1]
        copy_command = f"copy running-config tftp:/{tftpserver}{directorylocation}"
        print(f"Copycommand=: {copy_command}")
        output = net_connect.send_command_timing(copy_command)
        print("Try 1")
        if "Address or name" in output:
            output += net_connect.send_command_timing("\n")
            print("Try 2")
        if "Destination filename" in output:
            # output += net_connect.send_command_timing(f"{hostname}"+"_"+f"{device['host']}_"+"RunningConfig2"+"\n")
            output += net_connect.send_command_timing(f"{directorylocation}"+"/"+f"{hostname}_"+f"{filedatetime}_"+"RunningConfig"+"\n")
            print("Try 3")
            net_connect.disconnect
            
    except:
        print("An netmiko function exception occurred in tftpconfig(backuptftp.py)")
    
    finally:
        try:
            print(output)
        except:
            print("Could not print the output of the netmiko function tftpconfig(backuptftp.py)")

    print("Running the second try method")
    print(f"Directory = {directorylocation}")
    print(os.listdir(directorylocation))
    # print("ScanDir = "+ os.scandir(directorylocation))
    print(f"hostname={hostname}")
    print(f"device ip={device['host']}")
    print(f"{retname}")
    print("CURRENT DIRECTORY="+f"{os.getcwd()}")
    os.chdir(changetoDEVICEDATAdirectory)
    print("CURRENT DIRECTORY2="+f"{os.getcwd()}")

    
    if hostname == retname:
        print("Both names match !")
        try:
            for file_name in os.listdir(filesource):
                source=filesource+file_name
                destination=filedestination+"/"+hostname+"/"+file_name
                if os.path.isfile(source):
                    shutil.copy(source, destination)
        except OSError as error:
            print("Problem copying files to location = " + f"{error}")
        finally:
            if len(os.listdir(filesource)) > 0:
                print("FOLDER IS GREATER THAN O")
                # shutil.rmtree(filesource)
                for f in os.listdir(filesource):
                    os.remove(os.path.join(filesource, f))
            else:
                print("FOLDER IS NOT GREATER THAN 0")

    else:
        print("Both names DONOT match !")

'''


def tftpdevicebackup(self):
    
    devicesdetails = []
    self.tftpselectedid=StringVar()
    self.setbackuptypevar=StringVar() # Bakup type (startup, running)
    self.setfileidtypevar=StringVar() # File identifier (weekly, golden ?)

    sqliteConnection = sqlite3.connect(dbfile)
    cursor = sqliteConnection.cursor()
    #record = cursor.fetchall()
    query = (f"SELECT * FROM devices")
    cursor.execute(query)
    record = cursor.fetchall()
    for row in record:
        retid = row[0]
        retname = row[1]
        retip = row[2]
        retusername = row[3]
        retpassword = row[4]
        retdescription = row[5]
        rettype = row[6]
        retgolden = row[7]
        #retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getalldevices(self)
        devicesdetails.append(f"id:{retid}" + " " +  f"{retname}" + " " + f"({retip})")
        # print(f"{retid},{retname},{retip},{retusername},{retpassword},{retdescription},{rettype},{retgolden}")
        self.tftpselectedid.set(str(retid))
    
    def activatebackupbtn(self):
        if self.bktpeselecchkbx.get() !="Select" and self.fileidselec.get() !="Select":
            print("Both buton are not select")
            self.btnbackup.configure(state='normal', fg_color="green", hover_color='green4', border_width=2, border_color='dark green')
        else:
            print("A button must be select")
    
    def setbackuptype(self):
        self.setbackuptypevar.set(self.bktpeselecchkbx.get())
        if self.bktpeselecchkbx.get() =="Select":
            tk.messagebox.showinfo(title="Backup type selection error", message="Please select a valid backup type option.", icon='error', parent=self.tftpbackupwindow)
        else:
            self.devnotetxtbx.insert(INSERT, f"----------------------------------\nBackup type: {self.setbackuptypevar.get()}\n")
            # deactivate Type Identifier
            self.bktpeselectlbl.configure(state='disabled')
            self.bktpeselecchkbx.configure(border_color='gray', button_color='gray', fg_color="gray75", state='disabled')
            # self.bktpeselecchkbx.set(value="Select")
            self.bktpeselecbtn.configure(text="Accepted", fg_color="gray", state='disabled')
            # Activate File Identifier
            self.fileidlbl.configure(state='normal') # label
            self.fileidselec.configure(fg_color="pale green", state='readonly', border_color='dark slate gray', button_color='dark slate gray', button_hover_color='green4', dropdown_fg_color='pale green', dropdown_hover_color='PaleGreen3')
            self.fileidselec.set(value="Select")
            self.fileidbtn.configure(state='normal', fg_color="green", hover_color='green4')
        
            # This just asks if both coboboxes have a valid selection
            # activatebackupbtn(self)
 
            # put a feeedback here
            self.progstatus.set(0.2)
            # print(f"setbackuptype (353): {self.setbackuptypevar.get()}")
            self.editgrpstatuslbl.configure(text=f'{self.setbackuptypevar.get()} accepted')
    
    def setfileidtype(self):
        print("backuptftp.py fileidtype")
        self.setfileidtypevar.set(self.fileidselec.get())
        #self.devnotetxtbx.insert(INSERT, f"File identifier: {self.setbackuptypevar.get()}\n")
        #print(f"True or False or value : {self.fileidselec.state()}")

        if self.fileidselec.get() =="Select":
            tk.messagebox.showinfo(title="backup type", message="Please select a valid File identifier option.", icon='error', parent=self.tftpbackupwindow)
        else:
            self.devnotetxtbx.insert(INSERT, f"----------------------------------\nFile identifier: {self.fileidselec.get()}\n")
            # Deactivate File Identifier ('Daily','Weekly','Monthly','Golden','Silver')
            self.fileidlbl.configure(state='disabled')
            self.fileidselec.configure(border_color='gray', button_color='gray', fg_color="gray75", state='disabled')
            self.fileidbtn.configure(text="Accepted", fg_color="gray", state='disabled')
            # This just asks if both coboboxes have a valid selection
            activatebackupbtn(self)
            # put a feeedback here
            self.progstatus.set(0.3)
            self.editgrpstatuslbl.configure(text=f'File identified as: {self.setfileidtypevar.get()}')

    def setdevice(self):
        if self.devnamecbx.get() == "select a device":
            tk.messagebox.showinfo(title="Device selection error", message="Please select a valid Device option.", icon='error', parent=self.tftpbackupwindow)
        else:
            cbxid = re.findall(r'^\D*(\d+)', self.devnamecbx.get())
            cbxidstr = str(cbxid[0])
            # print(f"This is the selected item in the combobox: {cbxidstr}")
            retid1 = 0
            retname1 = ""
            retip1 = ""
            retusername1 = ""
            retpassword1 = ""
            retdescription1 = ""
            rettype1 = ""
            retgolden1 = ""
            
            retid1, retname1, retip1, retusername1, retpassword1, retdescription1, rettype1, retgolden1 = getdevicefromid(cbxidstr)
            print(f"{retid1},{retname1},{retip1},{retusername1},{retpassword1},{retdescription1},{rettype1},{retgolden1}")
            
            self.tftpselectedid.set(str(retid1))

            # DeActivate Device Selection
            self.devname.configure(state='disabled')
            self.devnamebtn.configure(text="Accepted", fg_color="gray", state='disabled') # button to select device once selected
            self.devnamecbx.configure(border_color='gray', button_color='gray', fg_color="gray75", state='disabled')
            
            # Activate Backup Type
            self.bktpeselectlbl.configure(state='normal')
            self.bktpeselecchkbx.configure(fg_color="pale green", state='readonly', border_color='dark slate gray', button_color='dark slate gray', button_hover_color='green4', dropdown_fg_color='pale green', dropdown_hover_color='PaleGreen3')
            self.bktpeselecchkbx.set(value="Select")
            self.bktpeselecbtn.configure(state='normal', fg_color="green", hover_color='green4')
            
            self.devnotelbl.configure(state='normal') # label
            self.devnotetxtbx.configure(state='normal', bg="light yellow", wrap=WORD) # label
            self.devnotetxtbx.insert(INSERT, f"Device id: {retid1}\n"+f"Device Name: {retname1}\n"+f"Device IP address: {retip1}\n")

            # put a feeedback here
            self.progstatus.set(0.1)
            self.editgrpstatuslbl.configure(text=f'{retname1} accepted')


    print("backuptftp.py / tftpdevicebackup")
    self.tftpbackupwindow = tk.Toplevel(master=self)
    self.tftpbackupwindow.title("Register")
    self.tftpbackupwindow.geometry('600x820+200+200')
    self.tftpbackupwindow.configure(background='powder blue')
    self.tftpbackupwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))
    
    # Disable root window when this one is active
    self.tftpbackupwindow.attributes('-topmost', 'true')
    self.tftpbackupwindow.transient(self)
    self.tftpbackupwindow.update_idletasks()
    self.tftpbackupwindow.grab_set()
    #self.tftpbackupwindow.wait_window()

    self.tftpbackupwindow.grid_columnconfigure(0, weight=1)
    # top title
    self.createlabel = ctk.CTkLabel(self.tftpbackupwindow, text="Backup a device configuration (TFTP)", font=('arial', 22, 'bold'))
    self.createlabel.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(20,16))
    self.separator1 = ttk.Separator(self.tftpbackupwindow, orient='horizontal')
    self.separator1.grid(row=1, sticky=EW, columnspan=3, padx=20, pady=10)
    
    # Device Selection ([1,RTR1, (1.1.1.1)],[2,SW1,(2.2.2.2)],)
    self.devname = Label(self.tftpbackupwindow, text="Device selection", font=('arial', 14, 'bold'), bg="powder blue", fg='black')
    self.devname.grid(row=2, column=0,sticky=W, padx=(25,10), pady=10)
    self.devnamecbx = ctk.CTkComboBox(self.tftpbackupwindow, width=220, font=('arial', 14, 'bold'), fg_color="pale green", values=devicesdetails, border_color='dark slate gray', button_color='dark slate gray', button_hover_color='green4', dropdown_fg_color='pale green', dropdown_hover_color='PaleGreen3', dropdown_font=('arial', 16, 'bold'))
    self.devnamecbx.grid(row=2, column=1, sticky=EW, padx=(15,10), pady=10)
    self.devnamecbx.set("select a device")
    self.devnamebtn = ctk.CTkButton(self.tftpbackupwindow, text="Set", width=100, fg_color="green", hover_color='green4', command=lambda:setdevice(self)) # command=grpcbxselc,
    self.devnamebtn.grid(row=2, column=2, padx=(15,25), pady=10)

    # Backup Type
    self.bktpeselectlbl = Label(self.tftpbackupwindow, text="Backup type", font=('arial', 14, 'bold'), bg="powder blue", fg='black', state=DISABLED)
    self.bktpeselectlbl.grid(row=3, column=0 ,sticky=W, padx=(25,10), pady=10)
    self.bktpeselecchkbxvalues = ['running-config', 'startup-config']
    self.bktpeselecchkbx = ctk.CTkComboBox(self.tftpbackupwindow, font=('arial', 14, 'bold'), state='disabled', values=self.bktpeselecchkbxvalues, dropdown_font=('arial', 16, 'bold'))
    self.bktpeselecchkbx.grid(row=3, column=1,sticky=EW, padx=(15,10), pady=10)
    self.bktpeselecbtn = ctk.CTkButton(self.tftpbackupwindow, text="Set", state=DISABLED, width=100, fg_color="gray", hover_color='green4', command=lambda:setbackuptype(self)) # , command=memselectfun
    self.bktpeselecbtn.grid(row=3, column=2, padx=(15,25), pady=10)
    
    # File Identifier
    self.fileidlbl = Label(self.tftpbackupwindow, text="File identifier", font=('arial', 14, 'bold'), bg='powder blue', fg='black', state=DISABLED)
    self.fileidlbl.grid(row=4, column=0,sticky=W, padx=(25,10), pady=10)
    self.fileidoptions = ['Daily','Weekly','Monthly','Golden','Silver']
    self.fileidselec = ctk.CTkComboBox(self.tftpbackupwindow, state='disabled', font=('arial', 14, 'bold'), values=self.fileidoptions, dropdown_font=('arial', 16, 'bold'))
    self.fileidselec.grid(row=4, column=1,sticky=EW, padx=(15,10), pady=10)
    self.fileidbtn = ctk.CTkButton(self.tftpbackupwindow, text="Set", state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'), command=lambda:setfileidtype(self)) #, command=confmethodselect
    self.fileidbtn.grid(row=4, column=2, padx=(15,25), pady=10)   

    # device Notes
    self.devnotelbl = Label(self.tftpbackupwindow, text="Notes", font='arial 14 bold', bg='powder blue', fg='black', state=DISABLED)
    self.devnotelbl.grid(row=5, column=0,sticky=NW, padx=25, pady=10)
    
    #grpdesetry = Text(ewindow, font='arial 14', height = 10, width = 25, bg="light yellow", wrap=WORD)
    self.devnotetxtbx = scrolledtext.ScrolledText(self.tftpbackupwindow, font='arial 12', height = 10, width = 25, bg="gray70", wrap=WORD, state=DISABLED)
    self.devnotetxtbx.grid(row=6, column=0, sticky=EW, padx=(25, 10), pady=(20), columnspan=2)
    # self.devnotetxtbtn = ctk.CTkButton(self.tftpbackupwindow, text="Set", state='disabled', width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold')) # command=grpcmdstxtbx, 
    # self.devnotetxtbtn.grid(row=6, column=2, sticky=S, padx=30, pady=10) 

    # self.separator2 = ttk.Separator(self.tftpbackupwindow, orient='horizontal')
    # self.separator2.grid(row=7, sticky=EW, columnspan=3, padx=20, pady=10)
    
    self.progstatus = ctk.CTkProgressBar(self.tftpbackupwindow, progress_color='green4', border_width=1, border_color='dark slate gray')
    self.progstatus.grid(row=7, column=0, sticky=EW, padx=30, pady=10, columnspan=3)
    self.progstatus.set(0)
    
    self.btnbackup = ctk.CTkButton(self.tftpbackupwindow, text="Backup", state='disabled', width=120, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'), command=lambda:tftpbtngo(self, self.tftpselectedid.get())) #, command=lambda:callchangegroupfile(self, *args, **kwargs)
    self.btnbackup.grid(column=0, row=8, pady=10, padx=(25,0), sticky=W)
    
    self.editgrpstatuslbl = Label(self.tftpbackupwindow, text="Select a device", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    self.editgrpstatuslbl.grid(row=8, column=1, padx=(0,55), pady=10, sticky=EW)
    
    self.btnexit = ctk.CTkButton(self.tftpbackupwindow, text="EXIT", width=120, fg_color="red2", font=('arial', 12, 'bold'), command=self.tftpbackupwindow.destroy, hover_color='red4', border_width=2, border_color='red4') # , command=onexitcleanup
    self.btnexit.grid(column=2, row=8, pady=10, padx=(0,25), sticky=E)
    self.cgouttxtbx = scrolledtext.ScrolledText(self.tftpbackupwindow, font='arial 10', height = 11, width = 25, bg="light goldenrod yellow", wrap=WORD, state=DISABLED)
    self.cgouttxtbx.grid(row=9, column=0,sticky=EW, padx=(25, 25), pady=10, columnspan=3)

    

backuptftp.py
jolders/devnetnode
from netmiko import ConnectHandler 
# from netmiko.ssh_exception import NetMikoAuthenticationException
# from netmiko.ssh_exception import NetMikoTimeoutException
# from netmiko.ssh_exception import SSHException
from netmiko.exceptions import NetMikoAuthenticationException
from netmiko.exceptions import NetMikoTimeoutException
from netmiko.exceptions import SSHException
import time
import os
import shutil
from tkinter import *
import tkinter as tk
from tkinter import ttk, messagebox
from tkinter.messagebox import showinfo
from tkinter import scrolledtext
import customtkinter as ctk
import os
from settings import *
import json
from connecttodb import getdevicefromid
from PIL import ImageTk, Image

# This is being used
def grpselectiongettftp(self, groupselection):
    # Group selection get values
    print("Groupselection (backuptftpGRP/grpselectionget()) get values")
    # What directory am I in
    print(f"1) The current working directory is (backuptftpGRP.py): {os.getcwd()}")

    # Change the directory to 'London routers' = 'devicegroups'
    os.chdir(devicegroups)
    groupdevicepath = os.path.join(devicegroups, groupselection)
    os.chdir(groupdevicepath)
    print(f"1a) The current working directory is (backuptftpGRP.py): {os.getcwd()}")
    # Get the json file
    filepath = os.path.join(groupselection + ".json")
    print(f"1b) (backuptftpGRP.py) filepath = {filepath}")
    filetoread = open(filepath)
    inputjsondata = json.load(filetoread)
    print(f"1c) Data in members: {inputjsondata['members']}")
    return inputjsondata['members']
    # Extract the data from the json file

# this is not being used
# !!!! delete this function
'''
def tftpsaveconfiggrp(self, groupselection):
    print(f"1a) This is tftpsaveconfiggrp (groupselection): {groupselection}")
    grpmemberreturntftpgrp = grpselectiongettftp(self, groupselection)
    print(f"3) These are the grp members this is tftpsaveconfiggrp (grpmemberreturn): {grpmemberreturntftpgrp}")
    print(f"4) This is tftpsaveconfiggrp (groupselection): {groupselection}")
    grpmemberreturn = grpselectiongettftp(self, groupselection)

    # Provide a id and get back everything
    for i in grpmemberreturn:
        retid=0
        retname=""
        retip=""
        retusername=""
        retpassword=""
        retdescription=""
        rettype=""
        retgolden=""
        
        print(i)
        getdevicefromid(i)
        
        try:
            retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(i)
        except TypeError:
                print(f"Printing load device grpselectiongettftp 'Try block is error exception' ")
        
        print(f"retid: {retid}")
        print(f"retname: {retname}")
        print(f"retip: {retip}")
        print(f"retusername: {retusername}")
        print(f"retpassword: {retpassword}")
        print(f"retdescription: {retdescription}")
        print(f"retdescription: {rettype}")
        print(f"retdescription: {retgolden}")

        # Now start the TFTP process
        #TFTP_SERVER = tftpserver
        hostname = ""
        filename = ""
        filedatetime = str(time.strftime("%Y-%m-%d-%H-%M-%S"))
        #directorylocation = "/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/"
        #filesource = '/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/'
        #filedestination = f'/Shared/NetDevApp3/cisco12ct/devicedata/{hostname}/'
        #changetoDEVICEDATAdirectory = '/Shared/NetDevApp3/cisco12ct'

        device = {
             'device_type': f'{rettype}',
             'host': f'{retip}',
             'username': f'{retusername}',
             'password': f'{retpassword}',
             'secret': f'{retpassword}'
             }
        print("--------------")
        print(device['host'])
        print("--------------")
        try:
            name = f"device{str(1)}"
            net_connect = ConnectHandler(**device)
            print("Try 0a")
            net_connect.enable()
            print(net_connect.find_prompt()[:-1])
            hostname = net_connect.find_prompt()[:-1]
            copy_command = f"copy {self.startuporrunning_var.get()} tftp://{tftpserver} {directorylocation}"
            output = net_connect.send_command_timing(copy_command)
            print("Try 1")
            if "Address or name" in output:
                output += net_connect.send_command_timing("\n")
                print("Try 2")
            if "Destination filename" in output:
                # output += net_connect.send_command_timing(f"{hostname}"+"_"+f"{device['host']}_"+"RunningConfig2"+"\n")
                output += net_connect.send_command_timing(f"{directorylocation}"+"/"+f"{hostname}_"+f"{filedatetime}_"+"RunningConfig"+"\n")
                print("Try 3")
                net_connect.disconnect
        
        except:
            print("An netmiko function exception occurred in tftpconfig(backuptftp.py)")
            
        finally:
            try:
                print(output)
            except:
                print("Could not print the output of the netmiko function tftpconfig(backuptftp.py)")
        
        print("Running the second try method")
        print(f"Directory = {directorylocation}")
        print(os.listdir(directorylocation))
        # print("ScanDir = "+ os.scandir(directorylocation))
        print(f"hostname={hostname}")
        print(f"device ip={device['host']}")
        print(f"{retname}")
        print("CURRENT DIRECTORY="+f"{os.getcwd()}")
        os.chdir(changetoDEVICEDATAdirectory)
        print("CURRENT DIRECTORY2="+f"{os.getcwd()}")

        if hostname == retname:
            print("Both names match !")
            try:
                for file_name in os.listdir(filesource):
                    source=filesource+file_name
                    destination=filedestination+"/"+hostname+"/"+file_name
                    if os.path.isfile(source):
                        shutil.copy(source, destination)
            except OSError as error:
                print("Problem copying files to location = " + f"{error}")
            finally:
                if len(os.listdir(filesource)) > 0:
                    print("FOLDER IS GREATER THAN O")
# shutil.rmtree(filesource)
                for f in os.listdir(filesource):
                    os.remove(os.path.join(filesource, f))
                else:
                    print("FOLDER IS NOT GREATER THAN O")
        else:
            print("Both names DONOT match !")
'''



def backupgrouptftp(self):
    print("(backuptfptgrp.py) backupgrouptftp")

    # GROUP COMBOBOX SELECTION DROPDOWN
    self.tftpgrpcombxstrgrp = []
    self.tftpgrpcombxstrgrp_var = ctk.StringVar(self)


    # The group is selected and the function gets a list of mebers id from the json file
    self.membersofselecgrp = []

    # Create a variable that will hold [id, hostname, ipaddress, username, password, type]
    self.selectedgrpbucket_var = []
    self.selectedgrpbucket_var_ids = []

    # Checkbox selection changes value 'startup-config' or 'running-config'
    self.startuporrunning_var = ctk.StringVar(self)

    # Switch on group selection combobox to on / off
    self.grpnameswitch_var = ctk.StringVar(self)

    # Switch on file identifier combobox on / off
    self.fileidswitch_var = ctk.StringVar(self)

    items = os.listdir(devicegroups)
    self.groupcomboboxoptions = []
    for item in items:
        if os.path.isdir(item):
            self.tftpgrpcombxstrgrp.append(item)
    
    def tftpgrpcombxstrgrp_callback(self):
        self.backuptftpgrptbl.delete(*self.backuptftpgrptbl.get_children())
        print(f"command=self.tftpgrpcombxstrgrp_callback: {self.grpnamecbx.get()}")
        self.tftpgrpcombxstrgrp_var.set(self.grpnamecbx.get())
        print(f"self.tftpgrpcombxstrgrp_var: {self.tftpgrpcombxstrgrp_var.get()}")

        # Get the details of the group
        self.membersofselecgrp = grpselectiongettftp(self, self.tftpgrpcombxstrgrp_var.get())
        print(f"members of the group is : {self.membersofselecgrp}")

        count = 0

        # provide members id to get the details of the members
        for i in self.membersofselecgrp:
            count += 1
            print(f"Getting group member with id: {i}")
            retid=0
            retname=""
            retip=""
            retusername=""
            retpassword=""
            retdescription=""
            rettype=""
            retgolden=""

            retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(i)
            print(f"retid: {retid}")
            print(f"retname: {retname}")
            print(f"retip: {retip}")
            print(f"retusername: {retusername}")
            print(f"retpassword: {retpassword}")
            print(f"retdescription: {retdescription}")
            print(f"retdescription: {rettype}")
            print(f"retdescription: {retgolden}")

            # Insert data into the treeview
            # Restricted description to 10 string characters (retdescription[:10])
            self.backuptftpgrptbl.insert(parent='', index='end', iid=count, text=count, values=(f'{retid}',f'{retname}',f'{retip}',f'{rettype}',f'{retdescription[:10]}'))
            #self.tftpbackupwindowgrp.geometry('680x820')

            # Enable services
            self.grpnameswitch.configure(state='normal', fg_color='yellow2', button_color='dark slate gray', border_color='dark slate gray', progress_color='pale green', text_color='black')

            # Status label update here
            self.editgrpstatuslbl.configure(text='Confirm selection')
        
    def grpnameswitch_event():
        print(f"grpnamebtn_event set button value = {self.grpnameswitch.get()}")
        print(f"grpnameswitch_var set button value = {self.grpnameswitch_var.get()}")

        # Switch = Locked
        if self.grpnameswitch_var.get() == 'on':
            print(f"grpnameswitch_var set button value 'on' = {self.grpnameswitch_var.get()}")
            
            # Disable groupselect combobox
            self.grpnamecbx.configure(state='disabled', fg_color="gray80", button_color='gray', border_color='gray')
            self.grpnamelbl.configure(state='disabled')
            self.backuptftpgrptbl.configure(style="dbgray.Treeview")

            # Enable check boxes & file identifier
            self.bktypelbl.configure(state='normal')
            self.chkbxrunningcfg.configure(state='normal', fg_color="green", hover_color="dark slate gray", border_color="dark slate gray", text_color_disabled="dark slate gray")
            self.chkbxstartupcfg.configure(state='normal', fg_color="green", hover_color="dark slate gray", border_color="dark slate gray", text_color_disabled="dark slate gray")
            self.fileidlbl.configure(state='normal')
            self.fileidselec.configure(state='normal', fg_color="yellow2", font=('arial', 14, 'bold'), border_color='dark slate gray', button_color='dark slate gray', dropdown_fg_color='khaki', border_width=2, dropdown_hover_color='khaki3', dropdown_font=('arial', 14, 'bold'))
            self.fileidswitch.configure(state='normal', fg_color="yellow2", button_color='dark slate gray', border_color='dark slate gray')


        # Switch = unlocked
        if self.grpnameswitch_var.get() == 'off':
            print(f"grpnameswitch_var set button value 'off' = {self.grpnameswitch_var.get()}")

            # Enable groupselect combobox
            self.grpnamecbx.configure(state='normal', fg_color="yellow2", button_color='dark slate gray', border_color='dark slate gray')
            self.grpnamelbl.configure(state='normal')
            self.backuptftpgrptbl.configure(style="db.Treeview")

            # Disable check boxes & file identifier
            self.bktypelbl.configure(state='disabled')
            self.chkbxrunningcfg.configure(state='disabled', fg_color="gray", hover_color="gray", border_color="gray", text_color_disabled="gray")
            self.chkbxstartupcfg.configure(state='disabled', fg_color="gray", hover_color="gray", border_color="gray", text_color_disabled="gray")
            self.fileidlbl.configure(state='disabled')
            self.fileidselec.configure(state='disabled', fg_color="gray80", button_color='gray', border_color='gray')
            self.fileidswitch.configure(state='disabled', fg_color='gray50', button_color='gray30', border_color='gray30', progress_color='gray30', text_color='gray20')

        count = 0
        # provide members id to get the details of the members
        for i in self.membersofselecgrp:
            count += 1
            print(f"Getting group member with id: {i}")
            retid=0
            retname=""
            retip=""
            retusername=""
            retpassword=""
            retdescription=""
            rettype=""
            retgolden=""

            retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(i)
            print(f"retid: {retid}")
            print(f"retname: {retname}")
            print(f"retip: {retip}")
            print(f"retusername: {retusername}")
            print(f"retpassword: {retpassword}")
            print(f"retdescription: {retdescription}")
            print(f"retdescription: {rettype}")
            print(f"retdescription: {retgolden}")

            self.selectedgrpbucket_var.append([retid, retname, retip, retusername, retpassword, rettype])
            self.selectedgrpbucket_var_ids.append(retid)

            print(self.selectedgrpbucket_var)
            print(self.selectedgrpbucket_var_ids)
            #print (strip('[').strip(']')(str(self.selectedgrpbucket_var_ids)))
            #self.outputxtbx.insert(INSERT, f"Bucket= {self.selectedgrpbucket_var}")

        # Disable groupselect combobox
        # self.grpnamecbx.configure(state='disabled')
        # Status label update here
        self.editgrpstatuslbl.configure(text=f'{self.tftpgrpcombxstrgrp_var.get()} selected')



    def btnbackup_event():
        print("btnbackup_event")
        print(self.selectedgrpbucket_var)
        self.outputxtbx.configure(state='normal')
        self.btnbackup.configure(state='disabled', fg_color='gray', border_color='gray40')
        self.fileidswitch.configure(state='disabled', fg_color='gray50', button_color='gray30', border_color='gray30', progress_color='gray', text_color='gray20')

        print(f"self.startuporrunning_var = {self.startuporrunning_var.get()}")

        count = 0
        self.update_idletasks()

        for i in self.selectedgrpbucket_var:
            bucketid = 0
            buckethostname = ""
            bucketip = ""
            bucketusername = ""
            bucketpassword = ""
            buckettype = ""

            bucketid = self.selectedgrpbucket_var[count][0]
            buckethostname = self.selectedgrpbucket_var[count][1]
            bucketip = self.selectedgrpbucket_var[count][2]
            bucketusername = self.selectedgrpbucket_var[count][3]
            bucketpassword = self.selectedgrpbucket_var[count][4]
            buckettype = self.selectedgrpbucket_var[count][5]

            print(f"id: {bucketid} hostname: {buckethostname} ip: {bucketip} username: {bucketusername} password: {bucketpassword} type: {buckettype}")
            count += 1

            device = {
                'device_type': f'{buckettype}',
                'host': f'{bucketip}',
                'username': f'{bucketusername}',
                'password': f'{bucketpassword}',
                'secret': f'{bucketpassword}'
                }
            
            print("--------------")
            print(device['host'])
            print("--------------")

            # Now start the TFTP process
            #TFTP_SERVER = tftpserver
            hostname = ""
            filename = ""
            filedatetime = str(time.strftime("%Y-%m-%d-%H-%M-%S"))
            #directorylocation = "/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/"
            #filesource = '/Shared/NetDevApp3/cisco12ct/devicedata/tftp_temp/'
            #filedestination = f'/Shared/NetDevApp3/cisco12ct/devicedata/{hostname}/'
            #changetoDEVICEDATAdirectory = '/Shared/NetDevApp3/cisco12ct'

            self.outputxtbx.insert(INSERT, f"Starting ***--- {buckethostname} ({bucketip}) ---*** \n")
            copy_command = (f"copy {self.startuporrunning_var.get()} tftp://{tftpserver} {directorylocation}")
            print(copy_command)

            try:
                net_connect = ConnectHandler(**device)
                print("Try Block: position 1")
                net_connect.enable()
                print(net_connect.find_prompt()[:-1])
                hostname = net_connect.find_prompt()[:-1]
                copy_command = (f"copy {self.startuporrunning_var.get()} tftp://{tftpserver}{directorylocation}")
                self.outputxtbx.insert(INSERT, f"Command:\n{copy_command}")
                output = net_connect.send_command_timing(copy_command)
                print("Try Block: position 2")
                if "Address or name" in output:
                    output += net_connect.send_command_timing("\n")
                    print("Try Block (IF): position 3a")
                if "Destination filename" in output:
                    # output += net_connect.send_command_timing(f"{hostname}"+"_"+f"{device['host']}_"+"RunningConfig2"+"\n")
                    output += net_connect.send_command_timing(f"{directorylocation}"+"/"+f"{hostname}_"+f"{filedatetime}_"+f"{self.startuporrunning_var.get()}_"+f"{self.fileidselec.get()}"+"\n")
                    print(f"OUTPUT=:{output}")
                    print("Try Block (IF): position 3b")
                    net_connect.disconnect
            except NetMikoAuthenticationException as AuthException:
                print("326 Authentication Error %s" % AuthException)
                self.outputxtbx.insert(INSERT, f"***--- {buckethostname} ({bucketip}) ---*** \n Authentication Error \n {AuthException}")
                continue
            except NetMikoTimeoutException as TimeoutException:
                #raise ConnectError(TimeoutException.args[0])
                print("331 No Valid Connections Error SSH connection 'USERNAME ?': %s" % TimeoutException)
                self.outputxtbx.insert(INSERT, f"Timeout Exception Error ***--- {buckethostname} ({bucketip}) ---*** \n {TimeoutException}")
                continue
            except SSHException as sshexception:
                print("An netmiko sshexception Error %s" % sshexception)
                self.outputxtbx.insert(INSERT, f"SSHException Error ***--- {buckethostname} ({bucketip}) ---*** \n {sshexception}")
                continue
            except Exception as unknown_error:
                print("339 An netmiko Unknown Error %s"+ str(unknown_error))
                self.outputxtbx.insert(INSERT, f"Unknown Error ***--- {buckethostname} ({bucketip}) ---*** \n  \n {unknown_error}")
                continue
            finally:
                if net_connect == True: net_connect.disconnect
                self.update_idletasks()
                self.outputxtbx.insert(INSERT, f"\nMoving file from temporary location.\n")
                self.outputxtbx.see('end')

            print("Running the second try method")
            print(f"Directory = {directorylocation}")
            print(os.listdir(directorylocation))
            # print("ScanDir = "+ os.scandir(directorylocation))
            print(f"hostname={hostname}")
            print(f"device ip={device['host']}")
            print(f"{buckethostname}")
            print("CURRENT DIRECTORY="+f"{os.getcwd()}")
            os.chdir(changetoDEVICEDATAdirectory)
            print("CURRENT DIRECTORY2="+f"{os.getcwd()}")

            if hostname == buckethostname:
                print("Both names match !")
                try:
                    for file_name in os.listdir(filesource):
                        source=filesource+file_name
                        destination=filedestination+"/"+hostname+"/"+file_name
                        self.outputxtbx.insert(INSERT, f"Filename:\n{file_name}\n")
                        if os.path.isfile(source):
                            shutil.copy(source, destination)
                            self.outputxtbx.insert(INSERT, f"Location:\n{filedestination}{hostname}\n")
                except OSError as error:
                    print("Problem copying files to location = " + f"{error}")
                    print("source = " + f"{source}")
                    print("filesource = " + f"{filesource}")
                    print("file_name = " + f"{file_name}")
                    print("destination = " + f"{destination}")
                    print("filedestination = " + f"{filedestination}")
                    print("hostname = " + f"{hostname}")
                finally:
                    self.outputxtbx.insert(INSERT, f"\n---*---*---*---*---*---*---*---*---*---*---*---*---*--- \n")
                    if len(os.listdir(filesource)) > 0:
                        print("FOLDER IS GREATER THAN O")
                        # shutil.rmtree(filesource)
                        for f in os.listdir(filesource):
                            os.remove(os.path.join(filesource, f))
                    else:
                        print("FOLDER IS NOT GREATER THAN 0")
            else:
                print("Both names DONOT match !")
        
        # close out the Lookup
        self.outputxtbx.configure(state='disabled')
        self.editgrpstatuslbl.configure(text='Task complete')


                

    # running-config checkbox
    def chkbxrunningcfg_event(self):
        print("chkbxrunningcfg running-config checkbox")
        self.startuporrunning_var.set("running-config")
        print(f"{self.startuporrunning_var.get()}")
        self.chkbxstartupcfg.deselect()
    
    # startup-config checkbox
    def chkbxstartupcfg_event(self):
        print("chkbxstartupcfg startup-config checkbox")
        self.startuporrunning_var.set("startup-config")
        print(f"{self.startuporrunning_var.get()}")
        self.chkbxrunningcfg.deselect()
    
    def fileidselec_callback(self):
        print("fileidselec_callback")
        self.editgrpstatuslbl.configure(text=f'Confirm "{self.fileidselec.get()}" selection')
        self.fileidselec.configure(width=220)

    def fileidswitch(self):
        print(f"fileidswitch value = {self.fileidswitch_var.get()}")
        self.fileidselec.configure(width=220)

        # Switch locked
        if self.fileidswitch_var.get() == 'on':
            print("The switch is locked")

            if self.fileidselec.get() == "Select" :
                print("Please select a valid File Identifier")
                messagebox.showinfo(parent=self.tftpbackupwindowgrp, title='Selection invalid', message='Please select a valid file identifier.')
                self.fileidswitch.deselect()
            
                
            else:
                # Disable Group selection switch
                # self.grpnameswitch.configure(state='normal')
                self.grpnameswitch.configure(state='disabled', progress_color='gray', fg_color='gray50', button_color='gray30', border_color='gray30')
                self.bktypelbl.configure(state='disabled')
                self.chkbxrunningcfg.configure(state='disabled', fg_color="gray", border_color="gray", text_color_disabled="gray")
                self.chkbxstartupcfg.configure(state='disabled', fg_color="gray", border_color="gray", text_color_disabled="gray")
                self.fileidlbl.configure(fg='gray')
                self.fileidselec.configure(state='disabled', fg_color="gray80", button_color='gray', border_color='gray')
                self.fileidswitch.configure(fg_color='yellow2')
                # not
                # self.fileidselec.configure(state='normal', fg_color="yellow2", font=('arial', 14, 'bold'), border_color='dark slate gray', button_color='dark slate gray', dropdown_fg_color='khaki', border_width=2, dropdown_hover_color='khaki3', dropdown_font=('arial', 14, 'bold'))

                print("You are good to activate the output and upload button.")

                # Activate the output textscroll
                self.outputxtbx.configure(state='normal', bg="light yellow")
                self.outputxtbx.insert(INSERT, f"Group: {self.tftpgrpcombxstrgrp_var.get()}\n")
                self.outputxtbx.insert(INSERT, f"Member ID's: {str(self.selectedgrpbucket_var_ids)[1:-1]}\n")
                self.outputxtbx.insert(INSERT, f"Backing up the: {self.startuporrunning_var.get()}\n")
                self.outputxtbx.insert(INSERT, f"File identified as: '{self.fileidselec.get()}'\n")
                self.outputxtbx.configure(state='disabled')


                # Activate the Backup process button
                self.btnbackup.configure(state='normal', fg_color='green', border_color='green4')

                self.editgrpstatuslbl.configure(text='Ready to run backup request')

                
            
        else:
            print("The switch is off")
            # Remove any text from output box
            self.outputxtbx.configure(state='normal', bg="light yellow")
            self.outputxtbx.delete('1.0', END)
            self.outputxtbx.configure(bg="gray64", state='disabled')
            self.btnbackup.configure(state='disabled', fg_color='gray', border_color='gray40')


        # Switch unlocked
        if self.fileidswitch_var.get() == 'off':
            print("The switch is NOT locked")
            self.grpnameswitch.configure(state='normal', progress_color='pale green', fg_color='yellow2', button_color='dark slate gray', border_color='dark slate gray')
            self.bktypelbl.configure(state='normal', fg='black')
            self.chkbxrunningcfg.configure(state='normal', fg_color="green", hover_color="dark slate gray", border_color="dark slate gray", text_color_disabled="dark slate gray")
            self.chkbxstartupcfg.configure(state='normal', fg_color="green", hover_color="dark slate gray", border_color="dark slate gray", text_color_disabled="dark slate gray")
            self.fileidlbl.configure(state='normal', fg='black')
            self.fileidselec.configure(state='normal', fg_color="yellow2", font=('arial', 14, 'bold'), border_color='dark slate gray', button_color='dark slate gray', dropdown_fg_color='khaki', border_width=2, dropdown_hover_color='khaki3', dropdown_font=('arial', 14, 'bold'))
            self.editgrpstatuslbl.configure(text='Unlocked for change')




    # create a window here
    self.tftpbackupwindowgrp = tk.Toplevel(master=self)
    self.tftpbackupwindowgrp.title("Backup group TFTP")
    self.tftpbackupwindowgrp.geometry('781x830+200+200')
    self.tftpbackupwindowgrp.minsize(500,800)
    self.tftpbackupwindowgrp.maxsize(900,850)
    self.tftpbackupwindowgrp.configure(background='powder blue')
    self.tftpbackupwindowgrp.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    # Disable root window when this one is active
    self.tftpbackupwindowgrp.attributes('-topmost', 'true')
    self.tftpbackupwindowgrp.transient(self)
    self.tftpbackupwindowgrp.update_idletasks()
    self.tftpbackupwindowgrp.grab_set()

    self.tftpbackupwindowgrp.grid_columnconfigure(0, weight=1)
    self.tftpbackupwindowgrp.grid_columnconfigure(1, weight=1)
    self.tftpbackupwindowgrp.grid_columnconfigure(2, weight=1)

    # top title
    self.createlabel = ctk.CTkLabel(self.tftpbackupwindowgrp, text="Backup a Group configuration (TFTP)", font=('arial', 22, 'bold'))
    self.createlabel.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(20,16))
    self.separator1 = ttk.Separator(self.tftpbackupwindowgrp, orient='horizontal')
    self.separator1.grid(row=1, sticky=EW, columnspan=3, padx=(40,10), pady=10)

    # Group Selection ()
    self.grpnamelbl = Label(self.tftpbackupwindowgrp, text="Group selection", font=('arial', 14, 'bold'), bg="powder blue", fg='black')
    self.grpnamelbl.grid(row=2, column=0, sticky=W, padx=(25,10), pady=10)
    self.grpnamecbx = ctk.CTkComboBox(self.tftpbackupwindowgrp, width=220, fg_color="yellow2", font=('arial', 14, 'bold'), border_color='dark slate gray', button_color='dark slate gray', dropdown_fg_color='khaki', border_width=2, dropdown_hover_color='khaki3', dropdown_font=('arial', 14, 'bold'), variable=self.tftpgrpcombxstrgrp_var, values=self.tftpgrpcombxstrgrp, command=lambda v:tftpgrpcombxstrgrp_callback(self))
    self.grpnamecbx.grid(row=2, column=1, sticky='w', padx=(20,5), pady=10)
    self.grpnamecbx.set("select a group")
    self.grpnameswitch = ctk.CTkSwitch(self.tftpbackupwindowgrp, text="Unlock | Lock", command=grpnameswitch_event, variable=self.grpnameswitch_var, 
                                             onvalue="on", offvalue="off", switch_width=40, switch_height=20, fg_color='gray50', 
                                             button_color='gray30', border_width=2, border_color='gray30', progress_color='pale green', text_color='gray20')
    self.grpnameswitch.grid(row=2, column=2)
    self.grpnameswitch.configure(state='disabled')

    
    #self.grpnamebtn = ctk.CTkButton(self.tftpbackupwindowgrp, text="Set", width=120, fg_color='green4', hover_color='green', font=('arial', 12, 'bold'), border_width=2, border_color='dark green', command=grpnamebtn_event)
    #self.grpnamebtn.grid(row=2, column=2, padx=(15,0), pady=10)

    # Treeview to show group (id, name, ip, filename)
    self.backuptftpgrptbl = ttk.Treeview(self.tftpbackupwindowgrp, selectmode='none', height=10, style="db.Treeview") #height=19, 
    self.backuptftpgrptbl.grid(row=3, column=0, sticky=EW, padx=(25,0), pady=10, columnspan=3)  #, expand=True, anchor='n', side='left'
    # Adding a vertical scrollbar to Treeview widget
    self.bktftpgrptblscroll = ttk.Scrollbar(self.tftpbackupwindowgrp, orient=tk.VERTICAL, command=self.backuptftpgrptbl.yview)
    self.backuptftpgrptbl.configure(yscrollcommand=self.bktftpgrptblscroll.set)
    self.bktftpgrptblscroll.grid(row=3, column=3, sticky='nes', padx=(0,30), pady=10)
    
    
    # Treeview config
    self.backuptftpgrptbl["columns"] = ("1", "2", "3", "4", "5")
    self.backuptftpgrptbl['show'] = 'headings'
    self.backuptftpgrptbl.column("1", width=50, anchor='center')
    self.backuptftpgrptbl.column("2", width=100, anchor='center')
    self.backuptftpgrptbl.column("3", width=100, anchor='center')
    self.backuptftpgrptbl.column("4", width=100, anchor='center')
    self.backuptftpgrptbl.column("5", width=200, anchor='center')
    self.backuptftpgrptbl.heading("1", text="id")
    self.backuptftpgrptbl.heading("2", text="hostname")
    self.backuptftpgrptbl.heading("3", text="ip")
    self.backuptftpgrptbl.heading("4", text="type")
    self.backuptftpgrptbl.heading("5", text="Description")

    # Reseting the geometry adjusts the treeview and scrollbar to fit
    self.tftpbackupwindowgrp.geometry('780x820')

    # Checkbox selection changes self.startuporrunning_var value
    self.bktypelbl = Label(self.tftpbackupwindowgrp, text="Backup Type", font=('arial', 14, 'bold'), bg='powder blue', fg='black', state='disabled')
    self.bktypelbl.grid(row=4, column=0,sticky=W, padx=(25,10), pady=10)
    self.chkbxrunningcfg = ctk.CTkCheckBox(self.tftpbackupwindowgrp, text="running-config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray", command=lambda:chkbxrunningcfg_event(self))
    self.chkbxrunningcfg.grid(row=4, column=1, pady=(10,10), padx=(40,20))
    self.chkbxstartupcfg = ctk.CTkCheckBox(self.tftpbackupwindowgrp, text="startup-config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray", command=lambda:chkbxstartupcfg_event(self))
    self.chkbxstartupcfg.grid(row=4, column=2, pady=(10,10), padx=(40,20))
    self.chkbxrunningcfg.select()
    self.startuporrunning_var.set("running-config")
    self.chkbxrunningcfg.configure(state='disabled')
    self.chkbxstartupcfg.configure(state='disabled')

    # File idenfifier
    # # File idenfifier Daily Weekly monthly Goldern ?
    # File Identifier
    self.fileidlbl = Label(self.tftpbackupwindowgrp, state='disabled', text="File identifier", font=('arial', 14, 'bold'), bg='powder blue', fg='black')
    self.fileidlbl.grid(row=5, column=0,sticky=W, padx=(25,10), pady=10)
    self.fileidoptions = ['Daily','Weekly','Monthly','Golden','Silver']
    self.fileidselec = ctk.CTkComboBox(self.tftpbackupwindowgrp, state='normal', font=('arial', 14, 'bold'), values=self.fileidoptions, dropdown_font=('arial', 16, 'bold'), command=lambda v:fileidselec_callback(self), width=220)
    self.fileidselec.grid(row=5, column=1,sticky=W, padx=(15,10), pady=10)
    self.fileidselec.set("Select")
    self.fileidselec.configure(state='disabled')
    self.fileidswitch = ctk.CTkSwitch(self.tftpbackupwindowgrp, text="Unlock | Lock", state='disabled', command=lambda:fileidswitch(self), variable=self.fileidswitch_var, onvalue="on", offvalue="off", switch_width=40, switch_height=20, fg_color='gray50', button_color='gray30', border_width=2, border_color='gray30', progress_color='pale green', text_color='gray20')
    self.fileidswitch.grid(row=5, column=2, padx=(15,25), pady=10)

    self.separator2 = ttk.Separator(self.tftpbackupwindowgrp, orient='horizontal')
    self.separator2.grid(row=6, sticky=EW, columnspan=3, padx=(40,0), pady=10)

    self.outputxtbx = scrolledtext.ScrolledText(self.tftpbackupwindowgrp, font='arial 12', height = 10, width = 25, bg="gray64", wrap=WORD, state='disabled') #bg="light goldenrod yellow"
    self.outputxtbx.grid(row=7, column=0, sticky=EW, padx=(25,0), pady=10, columnspan=3)

    self.progstatus = ctk.CTkProgressBar(self.tftpbackupwindowgrp, progress_color='green4', border_width=1, border_color='dark slate gray')
    self.progstatus.grid(row=8, column=0, sticky=EW, padx=(25,0), pady=5, columnspan=3)
    self.progstatus.set(0)

    self.btnbackup = ctk.CTkButton(self.tftpbackupwindowgrp, text="Backup", width=120, fg_color='gray', hover_color='green4', font=('arial', 12, 'bold'), border_width=2, border_color='gray40', command=btnbackup_event, state='disabled') 
    self.btnbackup.grid(row=9, column=0,  pady=5, padx=(25,1), sticky=W)
    self.editgrpstatuslbl = Label(self.tftpbackupwindowgrp, text="Select a Group", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    self.editgrpstatuslbl.grid(row=9, column=1, pady=5, sticky=W, padx=10) # padx=(0)
    self.btnexit = ctk.CTkButton(self.tftpbackupwindowgrp, text="EXIT", width=120, fg_color="red2", font=('arial', 12, 'bold'), command=self.tftpbackupwindowgrp.destroy, hover_color='red4', border_width=2, border_color='red4') # , command=onexitcleanup
    self.btnexit.grid(row=9, column=2, pady=5, padx=(0,0), sticky=E)







backuptftpgrp.py
jolders/devnetnode
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
from tkinter import scrolledtext
from settings import *

#Add Menu


def showcmdframe(self, choice, *args, **kwargs):
    # this shows the correct frame in the cmd window based on the combo selection.
    print("this is showcmdframe")
    print(f"combobox selected is :{choice}")
    # self.lblUpdate.config(text=f"Using: {connectselect} ")
    '''
    self.listOfCMDs = ['show version',
                       'show running-config',
                       'show ip interface brief',
                       'show ip route'
                       'show interface gigabitEthernet 0/0',
                       'show interface gigabitEthernet 0/1',
                       'show interface gigabitEthernet 0/2',
                       'show interface gigabitEthernet 0/3',
                       'show interface gigabitEthernet 0/4',
                       'show interface gigabitEthernet 1/0',
                       'show interface gigabitEthernet 1/1',
                       'show arp',
                       'show flash:'
                       'show vlan'
                       ]
    '''
    
                       
    '''
    def populatebox():
        for i in listOfCMDs:
            self.listBox.insert("end", i)
    '''
    

    
    if choice == "Paramiko":
        #self.statustext.set("Method: Paramiko")
        #if self.btncopy["state"] == "active" or "normal": self.btncopy["state"] = "disabled"
        #if self.btnpaste["state"] == "active" or "normal": self.btnpaste["state"] = "disabled"
        # self.btnpaste.config(state='disabled')

        self.btncopy.configure(state='disabled')
        self.btnpaste.configure(state='disabled')
        
        for widgets in self.cmdframea.winfo_children():
            widgets.destroy()
        
        self.cmdframecontainer = Frame(self.cmdframea, background="light sky blue")
        self.cmdframecontainer.pack(fill='both', expand=TRUE, anchor='n', side=TOP)

        #self.btnsipb=ttk.Button(self.cmdframea, text="Run the selected command", command=self.runcmdselection)
        #self.btnsipb.pack(side='left', anchor='s')
        
        self.listbox = Listbox(self.cmdframecontainer, 
                               activestyle='none', 
                               font=("Helvetica", 11, "bold"),
                               highlightthickness=0,
                               selectforeground='black',
                               background='light goldenrod yellow',
                               selectbackground="palegreen",
                               exportselection=False) # exportselection=False on combobox and listbox to clear conflict
        self.listbox.pack(side = LEFT, fill = 'both', expand=True, anchor='nw', padx=(15, 5), pady=(10, 5))
        self.scrollbar = Scrollbar(self.cmdframecontainer)
        self.scrollbar.pack(side = RIGHT, fill = BOTH)

        '''
        self.listbox.insert(0, 'show version')
        self.listbox.insert(1, 'show running-config')
        '''        
        # listOfCMDs located in the settigs file    
        for i in listOfCMDs:
            self.listbox.insert("end", i)
                
        self.listbox.select_set(1)
        self.cmdvarselected.set("show running-config")
        self.statusvarcmd2.set(value=self.cmdvarselected.get())
                
        self.listbox.config(yscrollcommand = self.scrollbar.set)
        self.scrollbar.config(command = self.listbox.yview)
        
        def localindex_selected(event):
            selected_indices = self.listbox.curselection()
            selected_index = selected_indices[0]
            selected_value = self.listbox.get(selected_index)
            self.cmdvarselected.set(selected_value)
            print(f"localindex_selected: {selected_value}")
            print(f"localindex_selected cmdvarselected value: {self.cmdvarselected.get()}")
            self.statusvarcmd2.set(value=self.cmdvarselected.get())  # Update status label
        
        self.listbox.bind('<<ListboxSelect>>', localindex_selected)
    
    elif choice == "Netmiko":
        #self.statustext.set("Method: Netmiko")
        self.btncopy.configure(state='disabled')
        self.btnpaste.configure(state='disabled')
        
        for widgets in self.cmdframea.winfo_children():
            widgets.destroy()
          
        self.cmdframecontainer = Frame(self.cmdframea, background="light sky blue")
        self.cmdframecontainer.pack(fill='both', expand=TRUE, anchor='n', side=TOP)

        #self.btnsipb=ttk.Button(self.cmdframea, text="Run the selected command", command=self.runcmdselection)
        #self.btnsipb.pack(side='left', anchor='s')
        
        self.listbox = Listbox(self.cmdframecontainer, 
                               activestyle='none', 
                               font=("Helvetica", 11, "bold"),
                               highlightthickness=0,
                               selectforeground='black',
                               background='light goldenrod yellow',
                               selectbackground="palegreen",
                               exportselection=False) # exportselection=False on combobox and listbox to clear conflict
        self.listbox.pack(side = LEFT, fill = 'both', expand=True, anchor='nw', padx=(15, 5), pady=(10, 5))
        self.scrollbar = Scrollbar(self.cmdframecontainer)
        self.scrollbar.pack(side = RIGHT, fill = BOTH)

        '''
        self.listbox.insert(0, 'show version')
        self.listbox.insert(1, 'show running-config')
        '''        
        # listOfCMDs located in the settigs file    
        for i in listOfCMDs:
            self.listbox.insert("end", i)
                
        self.listbox.select_set(1)
        self.cmdvarselected.set("show running-config")
        self.statusvarcmd2.set(value=self.cmdvarselected.get())
                
        self.listbox.config(yscrollcommand = self.scrollbar.set)
        self.scrollbar.config(command = self.listbox.yview)
        
        def localindex_selected(event):
            selected_indices = self.listbox.curselection()
            selected_index = selected_indices[0]
            selected_value = self.listbox.get(selected_index)
            self.cmdvarselected.set(selected_value)
            print(f"localindex_selected: {selected_value}")
            print(f"localindex_selected cmdvarselected value: {self.cmdvarselected.get()}")
            self.statusvarcmd2.set(value=self.cmdvarselected.get())  # Update status label
        
        self.listbox.bind('<<ListboxSelect>>', localindex_selected)
        
        #return self.cmdcmdvarselected.get()

    elif choice == "Netmiko_ch":
        #self.statustext.set("Method: Netmiko_ch")
        self.btncopy.configure(state='disabled')
        self.btnpaste.configure(state='disabled')
        
        for widgets in self.cmdframea.winfo_children():
            widgets.destroy()
          
        self.cmdframecontainer = Frame(self.cmdframea, background="light sky blue")
        self.cmdframecontainer.pack(fill='both', expand=TRUE, anchor='n', side=TOP)

        #self.btnsipb=ttk.Button(self.cmdframea, text="Run the selected command", command=self.runcmdselection)
        #self.btnsipb.pack(side='left', anchor='s')
        
        self.listbox = Listbox(self.cmdframecontainer, 
                               activestyle='none', 
                               font=("Helvetica", 11, "bold"),
                               highlightthickness=0,
                               selectforeground='black',
                               background='light goldenrod yellow',
                               selectbackground="palegreen",
                               exportselection=False) # exportselection=False on combobox and listbox to clear conflict
        self.listbox.pack(side = LEFT, fill = 'both', expand=True, anchor='nw', padx=(15, 5), pady=(10, 5))
        self.scrollbar = Scrollbar(self.cmdframecontainer)
        self.scrollbar.pack(side = RIGHT, fill = BOTH)
                
        '''
        self.listbox.insert(0, 'show version')
        self.listbox.insert(1, 'show running-config')
        '''        
        # listOfCMDs located in the settigs file    
        for i in listOfCMDs:
            self.listbox.insert("end", i)

        self.listbox.select_set(1)
        self.cmdvarselected.set("show running-config")
        self.statusvarcmd2.set(value=self.cmdvarselected.get())
                
        self.listbox.config(yscrollcommand = self.scrollbar.set)
        self.scrollbar.config(command = self.listbox.yview)
        
        def localindex_selected(event):
            selected_indices = self.listbox.curselection()
            selected_index = selected_indices[0]
            selected_value = self.listbox.get(selected_index)
            self.cmdvarselected.set(selected_value)
            print(f"localindex_selected: {selected_value}")
            print(f"localindex_selected cmdvarselected value: {self.cmdvarselected.get()}")
            self.statusvarcmd2.set(value=self.cmdvarselected.get())  # Update status label
        
        self.listbox.bind('<<ListboxSelect>>', localindex_selected)
        
    elif choice == "Netmiko_ch_config":
        #self.statustext.set("Method: Netmiko_ch_config")
        self.btncopy.configure(state='normal')
        self.btnpaste.configure(state='normal')
        
        for widgets in self.cmdframea.winfo_children():
            widgets.destroy()

        
        self.cmdframecontainer = Frame(self.cmdframea, background="light sky blue")
        self.cmdframecontainer.pack(fill='both', expand=TRUE, anchor='n', side=TOP)
        
        self.cmdtxtbox = scrolledtext.ScrolledText(self.cmdframecontainer, 
                                                   wrap=WORD, 
                                                   background='light goldenrod yellow', 
                                                   font=("Helvetica", 11, "bold"),
                                                   height=5) #, height=23, width=83
        self.cmdtxtbox.pack(side = LEFT, fill = 'both', expand=True, anchor='nw', padx=(15, 5), pady=(10, 5))
        
        # POP UP MENU
        def copy_select(): # copy selected text to clipboard
            global cmddata
            if self.cmdtxtbox.selection_get():
                cmddata=self.cmdtxtbox.selection_get() # copy selected text to clipboard
                self.clipboard_clear()
                self.clipboard_append(cmddata)
            
        def paste_select():
            global data
            self.cmdtxtbox.insert(tk.END,self.clipboard_get()) # Paste data from clipboard
        
        self.statusvarcmd2.set(value="Netmiko config multiline")  # Update status label
        
        
        #Add Menu
        popup = Menu(self.cmdtxtbox, tearoff=0)
        #Adding Menu Items
        popup.add_command(label="Copy", command=lambda:copy_select())
        popup.add_command(label="Paste", command=lambda:paste_select())
        
        def menu_popup(event):
        # display the popup menu
            try:
                popup.tk_popup(event.x_root, event.y_root, 0)
            finally:
                #Release the grab
                popup.grab_release()

        self.bind("<Button-3>", menu_popup)
    
    else: 
        #self.statustext.set("Method: Undefined")
        self.btncopy.configure(state='disabled')
        self.btnpaste.configure(state='disabled')
        
        for widgets in self.cmdframea.winfo_children():
            widgets.destroy()


        
        
        
        


        
        

cmdframebox.py
jolders/devnetnode
#from tkinter import *
#from tkinter import ttk
import sqlite3
from mysql.connector import Error
from settings import *

def getdevicefromid(rowIDrow): 
    print("I am 'getdevicefromid' row id: " + str(rowIDrow))
    try:
        sqliteConnection = sqlite3.connect(dbfile)
        cursor = sqliteConnection.cursor()
        #record = cursor.fetchall()
        query = (f"SELECT * FROM devices WHERE id = {rowIDrow}")
        cursor.execute(query)
        record = cursor.fetchall()
        for row in record:
            retid = row[0]
            retname = row[1]
            retip = row[2]
            retusername = row[3]
            retpassword = row[4]
            retdescription = row[5]
            rettype = row[6]
            retgolden = row[7]
            
        return retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden
        
    except sqlite3.Error as error:
        print("The error (connecttodb.py) message is: ", error)
        print("Exception class is: ", error.__class__)
        print("Exception is", error.args)




connecttodb.py
jolders/devnetnode
from tkinter import *
from tkinter import ttk
import tkinter
from tkinter import messagebox
from tkinter import scrolledtext
import customtkinter as ctk
import mysql.connector
from mysql.connector import Error
from passlib.context import CryptContext
import sqlite3
from settings import *
import os
from os.path import exists
import shutil
import json
from checklistcombobox import *
from PIL import ImageTk, Image


def createnewgroup(self, *args, **kwargs):
    rwindow = tkinter.Toplevel()
    rwindow.title("Register")
    rwindow.geometry('600x790+200+200')
    rwindow.resizable(False, False)
    rwindow.configure(background='powder blue')
    rwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    groupname = StringVar(value="Your Group Name")
    #members = StringVar(value="test_members")
    #devids = StringVar(value="test_devids")
    #groupcmdslist=[]
    configmethodvar = StringVar()
    
    # !
    # This is used to create the group selection dropdown selection
    # replace with self.groupcomboboxoptions
    #global memseleccbxvar
    # !

    global memselecchkbxvar
    self.memselecchkbxvar = []
    
    memvaluesdb = []
    
    global cmdenryvar
    cmdenryvar = []
    

    def changegroupfile(self, *args, **kwargs):
        # getinto current directory
        # print(f"The current working directory is : {os.getcwd()}")
        # print(f"The directory I want is: {directorypathroot}")
        # os.chdir(directorypathroot) # Obtained from settings file
        print(f"1) creategroupfile changegroupfile The cwd is : {os.getcwd()}")
        
        # Has the variables been captured
        print(f"2) Has the variables been captured {groupname.get()}")

        # change directory to project path
        # os.chdir(groudevicepath) # Obtained from settings file
        #print(f"The current working directory is : {os.getcwd()}")


        # HERE WE GO 
        print(f"3) GO (Group name: {groupname.get()})")
        print(f"4) GO (Member Selection: {self.memselecchkbxvar})")
        print(f"5) GO (Config Method: {configmethodvar.get()})")
        print(f"6) GO (Commands: {cmdenryvar})")

        # Get into the right folder
        groudevicepath = os.path.join(devicegroups, groupname.get())
        # load the file
        dest_path = os.path.join(groudevicepath, groupname.get() + ".json")
        filetoread = open(dest_path)
        inputjsondata = json.load(filetoread)
        print(f"7) Addbutton filetoread = {filetoread}")
        print(f"7b) Addbutton inputjsondata = {inputjsondata}")

        # change members selection
        inputjsondata['members'] = self.memselecchkbxvar
        print(f"8) Data in members: {inputjsondata['members']}")

        # change config method
        inputjsondata['method'].append(configmethodvar.get())
        print(f"9) Just want the first key in method 3: {inputjsondata['method']}")

        # change commands
        inputjsondata['commands'] = cmdenryvar
        #()
        print(f"10) Just want the first key commands: {inputjsondata['commands']}")

        # dump the file back
        print(f"11) I am 'inputjsondata' raw: {inputjsondata}")

        # OK Lets try try to change the data
        with open(dest_path, 'w', encoding='utf-8') as f:
            json.dump(inputjsondata, f, ensure_ascii=False, indent=4)

        # Close the file
        filetoread.close()

        # Make sure every button and combox is nulled
        # Member BUTTON and Input combobox
        memselecchkbx.configure(state='disabled') # Member Input combobox
        memselecbtn.configure(state='disabled', fg_color="gray")
        # ConfigMethod Button and Combobox
        confmethodselec.configure(state='disabled', fg_color="gray") # Method Combobox
        confbtn.configure(state='disabled', fg_color="gray")
        # Notes textbox and button
        grpdesetry.configure(state='disabled', bg="gray") # Notes textbox
        grpdesbtn.configure(state='disabled', fg_color="gray") # Button
        # Add Button
        btnadd.configure(state='disabled', fg_color="gray") # Button Add

        ########################### Status feedback
        creategrpstatuslbl.configure(text=f"New Group ({groupname.get()}) has been created")


    def populatemembx():
        print("Off to get the ID's to populate the checkbox selection")
        print("I'm going to fetch the list of IDs from the DB")
        try:
            sqliteConnection = sqlite3.connect(dbfile)
            cursor = sqliteConnection.cursor()
            query = (f"SELECT id FROM devices")
            cursor.execute(query)
            record = cursor.fetchall()
            result_list = [row[0] for row in record]
            result_list = list(map(str, result_list))
            print(f"creategroup/populatemembx = {result_list}")
            memvaluesdb = result_list
            #self.memseleccbxvar = result_list
            #print(f"memvaluesdb = {memvaluesdb}")
            memselecchkbx.config(values=memvaluesdb)
        
        except sqlite3.Error as error:
            print("The error (connecttodb.py(GETIDSFROMDB)) message is: ", error)
            print("Exception class is: ", error.__class__)
            print("Exception is", error.args)
    
    def grpprofilename():
        print("Set group profile name")
        # populate the members box with values
        populatemembx()
        #cgouttxtbx.insert(INSERT, f"1) Group name: {grpnameetry.get()}\n")
        grpnameetry.configure(state=DISABLED)
        grpnameetry.configure(fg_color="gray64")
        print(f"1)The current working directory is : {os.getcwd()}")
        print(f"2) The groupname is : {groupname.get()}")

        groudevicepath = os.path.join(devicegroups, groupname.get())

        if os.path.exists(groudevicepath):
            #print(f"Device already exists {groupname.get()}")
            grpnameetry.configure(state=NORMAL, fg_color="yellow2")
            messagebox.showinfo(title='Create Group', message=f'Group already exists: {groupname.get()}', icon='warning', parent=rwindow)


        else:
            print("Device doesn't exist")
            print(f"The devicepath directory is : {str(groudevicepath)}")
            # create file in project directory
            src_path = os.path.join(devicegroups, "jsonTemplate.json")
            dest_path = os.path.join(groudevicepath, groupname.get() + ".json")

        ####################################################################################
        # AT THIS POINT CREATE A Directory "C:\\Shared\\NetDevApp3\\cisco12ct\\devicegroups\\"
        # Also a blank template file .json is needed
        ####################################################################################

            if not os.path.exists(groudevicepath):
                os.mkdir(groudevicepath)
                print(f"New directory created : {groudevicepath}")

            print(f"sourcepath = {src_path}")
            print(f"desinationpath = {dest_path}")

            try:
                shutil.copy(src_path, dest_path)
                print("File copy was successfull")
            except:
                print("file copy failed")
        
        # read a file with parameters
        # Read file and load into memory
            filetoread = open(dest_path)
            inputjsondata = json.load(filetoread)
            inputjsondata['devicegroup']['groupname'] = groupname.get()
            print(f"1) I am 'inputjsondata' raw: {inputjsondata}")
            print(f"Try this see wahat happens: {inputjsondata}")
            print(f"Type of 'groupcomboboxoptions': {type(self.groupcomboboxoptions)}")
        #self.memseleccbxvar.append(groupname.get())
        #self.memseleccbxvar

        # OK Lets try try to change the data
            with open(dest_path, 'w', encoding='utf-8') as f:
                json.dump(inputjsondata, f, ensure_ascii=False, indent=4)
        
        # write to file with parameters
            print("json File altered(creategroup)")

        # Close the file
            filetoread.close()

        # Now activate the rest of the buttons
            cgouttxtbx.configure(state=NORMAL)
            cgouttxtbx.insert(INSERT, f"1) Group Created: {grpnameetry.get()}\n")
            cgouttxtbx.configure(state=DISABLED)
            grpnameetry.configure(state=DISABLED) #disabledbackground="green yellow"
            grpnamebtn.configure(state=DISABLED, fg_color="gray", text="Accepted")
            memselecbtn.configure(state=NORMAL, fg_color="green")
            memselecchkbx.configure(state='readonly')
            memselectlbl.configure(state=NORMAL)
            conflbl.configure(state=NORMAL)
            confbtn.configure(state=NORMAL, fg_color="green")
            confmethodselec.configure(state='readonly', fg_color="PaleGreen2", border_color='dark slate gray', button_color='dark slate gray')
            grpdesbtn.configure(state=NORMAL, fg_color="green")
            grpdesetry.configure(state=NORMAL)
            grpdesclbl.configure(state=NORMAL)
            grpdesetry.configure(background="light yellow")
        
            btnadd.configure(state=NORMAL, fg_color="green")
        


    def memselectfun():
        global memselecchkbxvar
        print("I am memselectfun")
        cgouttxtbx.configure(state=NORMAL)
        cgouttxtbx.insert(INSERT, f"2) Members selected: {memselecchkbx.get()}\n")
        cgouttxtbx.configure(state=DISABLED)
        print(type(memselecchkbx.get()))
        self.memselecchkbxvar = memselecchkbx.get()
        print(f"memselecchkbxvar value is : {self.memselecchkbxvar}")

    def confmethodselect():
        print("I am confmethodselect")
        cgouttxtbx.configure(state=NORMAL)
        cgouttxtbx.insert(INSERT, f"3) Config Method: {confmethodselec.get()}\n")
        cgouttxtbx.configure(state=DISABLED)
        configmethodvar.set(confmethodselec.get())
        print(f"configmethodvar value is : {configmethodvar.get()}")

    def grpcmdstxtbx():
        global cmdenryvar
        print("I am grpcmdstxtbx")
        inp = grpdesetry.get(1.0, "end-1c")
        cgouttxtbx.configure(state=NORMAL)
        cgouttxtbx.insert(INSERT, "4) Notes/Commands: " + "\n"+inp)
        cgouttxtbx.configure(state=DISABLED)
        cmdenryvar =  inp.splitlines()
        print(f"inp={inp}")
        print(f"cmdenryvar={cmdenryvar}")
        #cgouttxtbx.insert(INSERT, cmdenryvar)



    #rwindow.grid_rowconfigure(0, weight=1)
    rwindow.grid_columnconfigure(0, weight=1)
    # device name
    createlabel = ctk.CTkLabel(rwindow, text="Create a new Device Group", font=('arial', 22, 'bold'))
    createlabel.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(20,16))
    separator1 = ttk.Separator(rwindow, orient='horizontal')
    separator1.grid(row=1, sticky=EW, columnspan=3, padx=20, pady=10)
    
    # GROUP NAME
    grpname = ctk.CTkLabel(rwindow, text="Group name", font=('arial', 18, 'bold'))
    grpname.grid(row=2, column=0,sticky=W, padx=25, pady=10)
    grpnameetry = ctk.CTkEntry(rwindow, font=('arial', 14, 'bold'), fg_color="yellow2", justify='center', textvariable=groupname)
    grpnameetry.grid(row=2, column=1,sticky=EW, padx=10, pady=10)
    grpnamebtn = ctk.CTkButton(rwindow, text="Set", command=grpprofilename, width=100, fg_color="green", hover_color='green4', font=('arial', 12, 'bold'))
    grpnamebtn.grid(row=2, column=2, padx=30, pady=10) # the rest inherit padx=30
    grpnameetry.configure({"background": "PaleGreen2"})


    # MEMBER SELECTION
    memselectlbl = ctk.CTkLabel(rwindow, text="Member Selection", font=('arial', 18, 'bold'), state=DISABLED)
    memselectlbl.grid(row=3, column=0, sticky=W, padx=25, pady=10)
    memselecchkbx = ChecklistCombobox(rwindow, values=memvaluesdb, font=('arial 12 bold'), state='disabled', checkbutton_height=3, height=12, style='TCombobox')
    memselecchkbx.grid(row=3, column=1,sticky=EW, padx=10, pady=10)
    memselecbtn = ctk.CTkButton(rwindow, text="Set", command=memselectfun, state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    memselecbtn.grid(row=3, column=2)
    
    # CONFIGURATION COMBOBOX SELECTION
    conflbl = ctk.CTkLabel(rwindow, text="Config Method", font=('arial', 18, 'bold'), state=DISABLED)
    conflbl.grid(row=4, column=0,sticky=W, padx=25, pady=10)
    confmethodselec = ctk.CTkComboBox(rwindow, values=self.cmdcomboboxoptions, state='disabled', font=('arial', 14, 'bold'), dropdown_fg_color='PaleGreen2', dropdown_hover_color='PaleGreen3')
    confmethodselec.grid(row=4, column=1,sticky=EW, padx=10, pady=10)
    confbtn = ctk.CTkButton(rwindow, text="Set", command=confmethodselect, state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    confbtn.grid(row=4, column=2)

            

    # device description
    grpdesclbl = ctk.CTkLabel(rwindow, text="Notes", font=('arial', 18, 'bold'), state=DISABLED)
    grpdesclbl.grid(row=5, column=0,sticky=NW, padx=25, pady=10)
    
    #grpdesetry = Text(rwindow, font='arial 14', height = 10, width = 25, bg="light yellow", wrap=WORD)
    grpdesetry = scrolledtext.ScrolledText(rwindow, font='arial 12', height = 10, width = 25, bg="gray70", wrap=WORD, state=DISABLED)
    grpdesetry.grid(row=6, column=0,sticky=EW, padx=(25, 10), pady=10, columnspan=2)
    grpdesbtn = ctk.CTkButton(rwindow, text="Set", command=grpcmdstxtbx, state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    grpdesbtn.grid(row=6, column=2,sticky=S, padx=10, pady=10) 


    
    separator2 = ttk.Separator(rwindow, orient='horizontal')
    separator2.grid(row=7, sticky=EW, columnspan=3, padx=20, pady=10)
    btnadd = ctk.CTkButton(rwindow, text="Add", state=DISABLED, command=lambda:changegroupfile(self, *args, **kwargs), fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    btnadd.grid(column=0, row=8, pady=10)
    btnexit = ctk.CTkButton(rwindow, text="EXIT", command=rwindow.destroy,  fg_color="red2", hover_color='red3', font=('arial', 12, 'bold'))
    btnexit.grid(column=1, row=8, pady=10, padx=25)
    cgouttxtbx = scrolledtext.ScrolledText(rwindow, font='arial 10', height = 10, width = 25, bg="PaleGreen2", wrap=WORD, state=DISABLED)
    cgouttxtbx.grid(row=9, column=0,sticky=EW, padx=(25, 25), pady=10, columnspan=3)

    creategrpstatuslbl = Label(rwindow, text="", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    creategrpstatuslbl.grid(row=10, column=0, sticky=W, padx=(30, 20), pady=10, columnspan=3)
    
creategroup.py
jolders/devnetnode
from tkinter import *
from tkinter import ttk
import tkinter
from tkinter import messagebox
from tkinter import scrolledtext
import customtkinter as ctk
from settings import *
import os
from os.path import exists
import shutil
import json
from PIL import ImageTk, Image



def btndelete(self):
    print("I am btndelete")
    groudevicedir = os.path.join(devicegroups, self.grpnamecbx.get())
    respquest = messagebox.askyesno(title="Delete Group ?", message=f"Are you sure you want to delete {self.grpnamecbx.get()}", parent=self.dwindow)
    if respquest == True:
        print(f"The directory: {str(groudevicedir)} - gets deleted")
        self.btnreset.configure(state='disabled', fg_color='gray')
        self.btndelete.configure(state='disabled', fg_color='gray')
        
        try:
            shutil.rmtree(str(groudevicedir))

        except:
            self.deletegrpstatuslbl.configure(text="An error occured")
            
        self.grpnamecbx.configure(values=self.groupcomboboxoptions)
        self.deletegrpstatuslbl.configure(text=f"{self.grpnamecbx.get()} was deleted")
        self.cgouttxtbx.configure(state='normal') # Output textbox
        self.cgouttxtbx.configure(bg="gray80")
        self.cgouttxtbx.delete('1.0', END) #clear contents
        self.cgouttxtbx.configure(state='disabled')



    else:
        print("Else = Nothing happens")

    

def btnreset(self):
    print("I am btnreset")
    
    self.grpnamebtn.configure(state='normal', text="Set", fg_color='green') # Group select Button
    self.grpnamecbx.configure(state='readonly', fg_color='PaleGreen2') # Group select combobox
    self.cgouttxtbx.configure(state='disabled', bg="gray80") # Group select combobox
    self.cgouttxtbx.configure(state='normal') # Output textbox
    self.cgouttxtbx.configure(bg="gray80")
    self.cgouttxtbx.delete('1.0', END) #clear contents
    self.btnreset.configure(state='disabled', fg_color='gray')
    self.btndelete.configure(state='disabled', fg_color='gray')
    self.deletegrpstatuslbl.configure(text="")


def grpcbxselc(self):
    print("I am delete a group page 'delgroup' (deletegroup.py)")

    if self.grpnamecbx.get() == 'select Group to edit':
        print("please select a valid Group")
        self.deletegrpstatuslbl.configure(text="Please select a valid Group")
        return None

    # Disable the set button
    self.grpnamebtn.configure(state='disabled', fg_color='gray', text="Accepted") # Group select Button
    self.grpnamecbx.configure(state='disabled', fg_color="gray80") # Group select combobox
    self.cgouttxtbx.configure(state='normal') # Output textbox
    self.cgouttxtbx.configure(bg="PaleGreen2")
    
    self.btnreset.configure(state='normal', fg_color='DeepSkyBlue4') #
    self.btndelete.configure(state='normal', fg_color='dark green') #
    #self.btndelete.configure(fg_color="gray", state='normal')
    self.btnreset.configure(state='normal')
    self.deletegrpstatuslbl.configure(text=f"{self.grpnamecbx.get()} was chosen")






    print(f"1) the current working directory is : {os.getcwd()}")
    if os.getcwd() != devicegroups:
        os.chdir(devicegroups)
        print("cwd now changed to os.chdir(devicegroups)")
    
    print(f"1a) The current selected item is : {self.grpnamecbx.get()}")
    #print(f"2a) The groupname is : {self.groupname.get()}")
    groudevicedir = os.path.join(devicegroups, self.grpnamecbx.get())
    print(f"1a) The groudevicedir value is : {str(groudevicedir)}")

    grpfilepath = os.path.join(groudevicedir, self.grpnamecbx.get() + ".json")
    grpfiletoread = open(grpfilepath)
    grpjsondata = json.load(grpfiletoread)
    print(f"1) I am 'inputjsondata' raw: {grpjsondata}")

    # Insert members into scrollbox
    self.cgouttxtbx.insert(INSERT, f"1) Group name is : {self.grpnamecbx.get()}\n")
    self.cgouttxtbx.insert(INSERT, f"2) The directory is : {groudevicedir}\n")
    self.cgouttxtbx.insert(INSERT, f"3) Config file is : {grpfilepath}\n")
    self.cgouttxtbx.insert(INSERT, f"4) Method config is : {grpjsondata['members']}\n")
    self.cgouttxtbx.insert(INSERT, f"5) Method config is : {grpjsondata['method']}\n")
    self.cgouttxtbx.insert(INSERT, f"6) Notes/Commands config is : {grpjsondata['commands']}\n")
    

    items = os.listdir(devicegroups)
    self.groupcomboboxoptions = []
    for item in items:
        if os.path.isdir(item):
            self.groupcomboboxoptions.append(item)
    
    self.cgouttxtbx.configure(state="disabled", bg="PaleGreen2")



def delgroup(self):
    self.dwindow = tkinter.Toplevel()
    self.dwindow.title("Delete Device Group")
    self.dwindow.geometry('600x477+200+200')
    self.dwindow.resizable(False, False)
    self.dwindow.configure(background='powder blue')
    self.dwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    self.dwindow.grid_columnconfigure(0, weight=1)
    # device name
    self.createlabel = ctk.CTkLabel(self.dwindow, text="Delete a Device Group", font=('arial', 22, 'bold'))
    self.createlabel.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(20,16))
    self.separator1 = ttk.Separator(self.dwindow, orient='horizontal')
    self.separator1.grid(row=1, sticky=EW, columnspan=3, padx=20, pady=10)

    # GROUP NAME (Group1, London Routers, WestCoastSwitches)
    self.grpname = ctk.CTkLabel(self.dwindow, text="Group name", font=('arial', 18, 'bold'))
    self.grpname.grid(row=2, column=0,sticky=W, padx=35, pady=10)
    self.grpnamecbx = ctk.CTkComboBox(self.dwindow, values=self.groupcomboboxoptions,  font=('arial', 14, 'bold'), fg_color="pale green", width=230, border_color='green4', 
                                      button_color='green4', button_hover_color='green', dropdown_fg_color='pale green', dropdown_hover_color='PaleGreen3')
    self.grpnamecbx.grid(row=2, column=1, sticky=EW, padx=10, pady=10)
    self.grpnamecbx.set("select Group to edit")
    self.grpnamebtn = ctk.CTkButton(self.dwindow, text="Set", command=lambda:grpcbxselc(self), width=100, fg_color="green", hover_color='green4')
    self.grpnamebtn.grid(row=2, column=2, padx=30, pady=10)

    self.cgouttxtbx = scrolledtext.ScrolledText(self.dwindow, font='arial 10', height = 10, width = 25, bg="gray80", wrap=WORD)
    self.cgouttxtbx.grid(row=4, column=0,sticky=EW, padx=(25, 25), pady=30, columnspan=3)

    self.separator2 = ttk.Separator(self.dwindow, orient='horizontal')
    self.separator2.grid(row=5, sticky=EW, columnspan=3, padx=20, pady=10)

    self.btnreset = ctk.CTkButton(self.dwindow, text="Reset", command=lambda:btnreset(self),  fg_color="gray", state='disabled', hover_color='DeepSkyBlue3', font=('arial', 12, 'bold'), width=100)
    self.btnreset.grid(column=0, row=6, pady=10, padx=25)

    self.btndelete = ctk.CTkButton(self.dwindow, text="Delete", command=lambda:btndelete(self),  fg_color="gray", state='disabled', hover_color='green4', font=('arial', 12, 'bold'), width=100)
    self.btndelete.grid(column=1, row=6, pady=10, padx=25)

    self.btnexit = ctk.CTkButton(self.dwindow, text="EXIT", command=self.dwindow.destroy,  fg_color="red2", hover_color='red3', font=('arial', 12, 'bold'), width=100)
    self.btnexit.grid(column=2, row=6, pady=10, padx=25)

    self.deletegrpstatuslbl = Label(self.dwindow, text="", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    self.deletegrpstatuslbl.grid(row=7, column=1, sticky=W, padx=(20, 20), pady=10)

    

deletegroup.py
jolders/devnetnode
import sqlite3
from tkinter import *
import tkinter as tk
from tkinter.ttk import Treeview
from tkinter import ttk, messagebox
from tkinter.filedialog import askopenfilename
import customtkinter as ctk
from settings import *
from PIL import ImageTk, Image
#from backuptftpgrp import tftpsaveconfiggrp
# from uploadtftp import configuploadtftp
import os


# Collect and display the devices database
def fetchalldb(self, devtblframea, iam_selecteddevice):
    devtblframea = self.devtblframea
    # print("I am fetchalldb.py")
    # Treeview styling
    style = ttk.Style()
    style.theme_use("clam") #'aqua', 'step', 'clam', 'alt', 'default', 'classic'
    style.configure("db.Treeview", background="pale green", relief="flat", rowheight="25")
    style.configure("db.Treeview.Heading", background="PaleGreen3", foreground="black", font=('Helvetica 10 bold'))
    style.configure("dbgray.Treeview", background="gray60", relief="flat", rowheight="25")
    style.configure("dbgray.Treeview.Heading", background="gray50", foreground="black", font=('Helvetica 10 bold'))
    ttk.Style().map("dbgray.Treeview.Heading", background = [('active', "gray40")])
    ttk.Style().map("Treeview.Heading", background = [('pressed', '!focus', "SlateGray3"),('active', "chartreuse3"),('disabled', '#ffffff')])
    style.map("Treeview", background=[('selected', "PaleGreen4")])

    self.my_canvas = Canvas(self.devtblframea)
    self.my_canvas.pack(side="left", fill=BOTH, expand=1, padx=2, pady=2) # shows a border arround the treeview devices "yellow"
    self.tree = ttk.Treeview(self.my_canvas, selectmode='browse', style="db.Treeview") 
    self.tree.pack(side="bottom", fill=BOTH, expand=1) #side='left', expand=TRUE, fill=BOTH, anchor=NW
    #tree.pack_propagate(0)
    self.vsb = ttk.Scrollbar(self.devtblframea, orient="vertical", command=self.tree.yview) #width=20
    self.vsb.pack(side='right', expand=FALSE, fill=BOTH, anchor=NW)
    #vsb.pack_propagate(0)
    self.tree.configure(yscrollcommand=self.vsb.set)

    con = sqlite3.connect(dbfile)
    cursor = con.cursor()
    cursor.execute("SELECT * FROM devices")
    rows = cursor.fetchall()
    self.tree["columns"] = ("1", "2", "3", "4", "5", "6", "7", "8")
    self.tree['show'] = 'headings'
    self.tree.column("1", width=5, anchor='c')
    self.tree.column("2", width=10, anchor='c')
    self.tree.column("3", width=10, anchor='c')
    self.tree.column("4", width=10, anchor='c')
    self.tree.column("5", width=10, anchor='c')
    self.tree.column("6", width=20, anchor='c')
    self.tree.column("7", width=20, anchor='c')
    self.tree.column("8", width=20, anchor='c')
    self.tree.heading("1", text="id")
    self.tree.heading("2", text="name")
    self.tree.heading("3", text="ip")
    self.tree.heading("4", text="username")
    self.tree.heading("5", text="password")
    self.tree.heading("6", text="description")
    self.tree.heading("7", text="type")
    self.tree.heading("8", text="golden")

    for item in self.tree.get_children():
        self.tree.delete(item)
        print("db table display deleted")

    #print(row)
    for record in rows:
        self.tree.insert("",'end', iid=record[0], values=(record[0],record[1],record[2],record[3],record[4],record[5][:22],record[6],record[7]))
    
    firstselec = self.tree.get_children()[0]
    print (f"1) fetchdb.py -The firstselect item is : {firstselec}")
    self.tree.selection_set(firstselec)
    cursor.close()
    con.close()
    
    def selectItem(a):
        #global iam_selecteddevice
        try:
            selected_item = self.tree.selection()
            treeview_id = self.tree.item(selected_item)['values'][0]
            #print(f"I am DEF selectItem {treeview_id}")
            self.iam_selecteddevice.set(value=treeview_id)
            #floatingiam.set(value=treeview_id)
            # statusvargrp is the status value on the statusoutput frame
            self.statusvargrp.set(str(self.iam_selecteddevice.get()))

        except:
            print("No selectItem error in fetchdb.py line 75")

    
    def rightClickMenu(event):
        print("1) devicecrud.py (rightClickMenu) Right click menu")

        def helloedit():
            print(f"hello! edit: {iam_selecteddevice.get()}")
            #changedbdevice(iam_selecteddevice)
            #print(f"Printing changethisdevice function={self.iam_selecteddevice.get()}")
            self.rdwindow = ctk.CTkToplevel()
            self.rdwindow.grab_set()
            changedbdevice(self, self.rdwindow, self.iam_selecteddevice.get())

        def hellodelete():
            print(f"hello! delete: {iam_selecteddevice.get()}")
            deletedbdevice(self, self.iam_selecteddevice.get())

        def hellocreate():
            print(f"hello! greate new device")
            self.cdwindow = ctk.CTkToplevel()
            self.cdwindow.grab_set()
            createdevice(self, self.cdwindow)

        def hellocreatedir():
            print(f"hello! delete: {iam_selecteddevice.get()}")
            getdevicename = []
            thisdevicename = ""
            getdevicename = self.tree.item(self.tree.focus())
            thisdevicename = getdevicename['values'][1]
            print(f"thisdevicename = {thisdevicename}")
            print(type(thisdevicename))


            devicepath = os.path.join(devicesaveddata, thisdevicename)
            print(f"2) The devicepath directory is (filesdb.py): {devicepath}")
            
            if not os.path.exists(devicepath):
                os.mkdir(devicepath)
                print(f"4) New directory created (filesdb.py): {devicepath}")
                messagebox.showinfo(parent=self.my_canvas, title='Directory creation', message=f'A directory was created.\n{devicepath}')
                return

            if os.path.exists(devicepath):
                messagebox.showinfo(parent=self.my_canvas, title='Directory creation', message=f'Directory already exists.\n(no creation necessary)\n{devicepath}')
                return

        
    # create a popup menu
    # print(event.x, event.y)
        rowID = self.tree.identify('item', event.x, event.y)
        rowIDrow = self.tree.identify_row(event.y)
        getdevicename = self.tree.item(self.tree.focus())
        thisdevicename = getdevicename['values'][1]
        print("Iam rowIDrow " + rowIDrow)
        if rowID:
            menu = Menu(self.devtblframea, tearoff=0)
            menu.add_separator()
            menu.add_command(label=f"Edit id:{rowID} ({thisdevicename})", command=helloedit)
            menu.add_command(label=f"Delete id:{rowID} ({thisdevicename})", command=hellodelete)
            menu.add_separator()
            menu.add_command(label=f"Create directory id:{rowID} ({thisdevicename})", command=hellocreatedir)
            menu.add_separator()
            menu.add_command(label=f"Create new device", command=hellocreate)
            #menu.add_command(label=f"Add to group ('id' {rowID})", command=groupadd)
            #menu.add_command(label=f"Download config from device ('id' {rowID})", command=tftpconfig)
            #menu.add_command(label=f"Upload config to device ('id' {rowID})", command=tftupload)

            self.tree.selection_set(rowID)
            self.tree.focus_set()
            self.tree.focus(rowID)
        # print("I am 3 " + rowID)
            #menu.post(event.x_root, event.y_root)
            menu.tk_popup(event.x_root, event.y_root)
            self.vsb.update()
        else:
            pass

    self.treebind = self.tree.bind("<<TreeviewSelect>>", selectItem)
    self.tree.bind("<Button-3>", rightClickMenu)
    #treebind = tree.bind("<<TreeviewSelect>>", selectItem)
    #treebind = tree.bind("<<TreeviewSelect>>", getselecteddevice2, add="+")
    ## tree.bind('<<TreeviewSelect>>', getselecteddevice)
    #tree.bind("<Button-3>", rightClickMenu)

# Destroy and re-create the devices database
def clear_all(self):
    #print("(fetchdb.py) clear_all has been accessed")
    try:
        for item in self.tree.get_children():
            self.tree.delete(item)
            self.tree.forget()
            self.vsb.forget()
            self.my_canvas.forget()
    except:
        print("(fetchdb.py) Exception")
    finally:
        # Recrete the fetchall command
        fetchalldb(self, self.devtblframea, self.iam_selecteddevice)

# Create new entry in the devices database
def createdevice(self, cdwindow):
    self.rname = StringVar(self)
    self.rip = StringVar(self)
    self.rusername = StringVar(self)
    self.rpassword = StringVar(self)
    self.rdescription = StringVar(self)
    self.rtype = StringVar(self)
    self.rgfilename = StringVar(self)

    #self.cdwindow = ctk.CTkToplevel()
    self.cdwindow.title("Register (createdevice)")
    self.cdwindow.geometry('500x720+200+200')
    self.cdwindow.attributes('-topmost', 'true')
    self.cdwindow.configure(fg_color="light blue")
    #self.after(250, lambda: self.iconbitmap('pythonicon.ico'))
    #cdwindow = ctk.toplevel_window.focus()
    #cdwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))
    self.cdwindow.after(250, lambda: self.cdwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon)))))
    self.grid_rowconfigure(0, weight=1)
    self.grid_columnconfigure(0, weight=1)

    self.createlabel = ctk.CTkLabel(self.cdwindow, text="Create a new device", font=("arial", 22, "bold"))
    self.createlabel.grid(row=0, column=0, sticky="ew", columnspan=3, pady=15)
    self.separator1 = ttk.Separator(self.cdwindow, orient='horizontal')
    self.separator1.grid(row=1, sticky="ew", columnspan=3, padx=(30, 30), pady=10)

    # device Name
    self.dbname = ctk.CTkLabel(self.cdwindow, text="Device name", font=("arial", 18, "bold"), justify='left')
    self.dbname.grid(row=2, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbname = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width=230, fg_color="light yellow", textvariable=self.rname) 
    self.ebdbname.grid(row=2, column=1, sticky='w', padx=(30, 30), pady=10)
    # device IP Address
    self.dbip = ctk.CTkLabel(self.cdwindow, text="IP Address", font=("arial", 18, "bold"), justify='left')
    self.dbip.grid(row=3, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbip = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width=230, fg_color="light yellow", textvariable=self.rip) #textvariable=rname
    self.ebdbip.grid(row=3, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Username
    self.dbusername = ctk.CTkLabel(self.cdwindow, text="Username", font=("arial", 18, "bold"), justify='left')
    self.dbusername.grid(row=4, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbusername = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.rusername) #textvariable=rname
    self.ebdbusername.grid(row=4, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Password
    self.dbpassword = ctk.CTkLabel(self.cdwindow, text="Password", font=("arial", 18, "bold"), justify='left')
    self.dbpassword.grid(row=5, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbpassword = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.rpassword) #textvariable=rname
    self.ebdbpassword.grid(row=5, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Description
    self.dbdescription = ctk.CTkLabel(self.cdwindow, text="Description", font=("arial", 18, "bold"), justify='left')
    self.dbdescription.grid(row=6, column=0, sticky='nw', padx=(30, 10), pady=10)
    self.ebdbdescription = ctk.CTkTextbox(self.cdwindow, font=("arial", 14), height=200, width=230, fg_color="light yellow", border_width=2, wrap='word') #textvariable=rname
    self.ebdbdescription.grid(row=6, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Type
    self.dbtype = ctk.CTkLabel(self.cdwindow, text="Type", font=("arial", 18, "bold"), justify='left')
    self.dbtype.grid(row=7, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbtype = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.rtype) #textvariable=rname
    self.ebdbtype.grid(row=7, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Goldern filename
    self.dbgfilename = ctk.CTkLabel(self.cdwindow, text="Goldern filename", font=("arial", 18, "bold"), justify='left', text_color="gray")
    self.dbgfilename.grid(row=8, column=0, sticky='w', padx=(30, 10), pady=10)
    self.btnfile = ctk.CTkButton(self.cdwindow, state="disabled", text="File select", font=("arial", 14), fg_color="gray", text_color="black")
    self.btnfile.grid(row=8, column=1, pady=10, padx=(30, 30))
    self.ebdbgfilename = ctk.CTkEntry(self.cdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.rgfilename)
    self.ebdbgfilename.grid(row=9, column=0, sticky='ew', padx=(30, 30), pady=10, columnspan=2)
    
    self.rgfilename.set(value="'Edit' device after creation")
    self.ebdbgfilename.configure(state='disabled', fg_color='gray70')
    
    self.separator2 = ttk.Separator(self.cdwindow, orient='horizontal')
    self.separator2.grid(row=10, sticky="ew", columnspan=3, padx=(30, 30), pady=10)
    self.btngo = ctk.CTkButton(self.cdwindow, text="Create", font=("arial", 14, "bold"), fg_color="green3", hover_color="green4", border_color='green4', border_width=2, text_color="black", command=lambda:insertdevice(self))# 
    self.btngo.grid(row=11, column=0, pady=10, padx=30, sticky='w')
    self.btnexit = ctk.CTkButton(self.cdwindow, text="EXIT", font=("arial", 14, "bold"), fg_color="orange red", hover_color="OrangeRed3", text_color="black", border_width=2, border_color='OrangeRed3', command=self.cdwindow.destroy)
    self.btnexit.grid(row=11, column=1, pady=10, padx=30, sticky='e')
    self.lblcds = ctk.CTkLabel(self.cdwindow, text="", font=("arial", 18, "bold"), justify='left')# 
    self.lblcds.grid(row=12, column=0, pady=10, padx=(30, 10), columnspan=2)

    '''
    def filedialogopen(self):
        print("filedialogopen")
        filename = askopenfilename(parent=self.cdwindow, title="Base File selection")
        print(f"filedialogopen = {filename}")
    '''

    def insertdevice(self):
        #self.lblcds.configure(text="Hello")
        #print(f"Hello 'dosomething' IamSelecteddevice {self.iam_selecteddevice}")
        self.btngo.configure(state="disabled", fg_color="gray", border_color='gray')

        try:
            sqliteConnection = sqlite3.connect(dbfile)
            cursor = sqliteConnection.cursor()
            #print("1) (createdevice.py) Successfull connection to db")
            query = "INSERT INTO devices (id,name,ip,username,password,description,type,golden) VALUES(NULL, ?, ?, ?, ?, ?, ?, ?)"
            cursor.execute(query,(str(self.ebdbname.get()),str(self.ebdbip.get()),str(self.ebdbusername.get()),str(self.ebdbpassword.get()),str(self.ebdbdescription.get('1.0','end-1c')),str(self.ebdbtype.get()),str(self.ebdbgfilename.get())))
            sqliteConnection.commit()
        except sqlite3.Error as error:
            self.lblcds.configure(text_color="red")
            self.lblcds.configure(text=f"Creation error: {error}")
            #print("The error message is: ", error)
            #print("Exception class is: ", error.__class__)
            #print("Exception is", error.args)
        finally:
            # Cleanup time
            if sqliteConnection:
                sqliteConnection.close()

            # Strip the variables down after use
            #self.rname.set("")
            #self.rip.set("")
            #self.rusername.set("")
            #self.rpassword.set("")
            #self.rdescription.set("")
            #self.rtype.set("")
            #self.rgfilename.set("")

            # Strip the entry boxes down after use
            #self.ebdbname.delete(0, END) # Device name
            #self.ebdbip.delete(0, END) # Device ip
            #self.ebdbusername.delete(0, END) # username
            #self.ebdbpassword.delete(0, END) # password
            #self.ebdbdescription.delete('1.0', END)
            #self.ebdbtype.delete(0, END) # type
            #self.ebdbgfilename.delete(0, END) # filename

            # Change text and entry boxe to gray
            self.ebdbname.configure(state='disabled', fg_color="light gray")
            self.ebdbip.configure(state='disabled', fg_color="light gray")
            self.ebdbusername.configure(state='disabled', fg_color="light gray")
            self.ebdbpassword.configure(state='disabled', fg_color="light gray")
            self.ebdbdescription.configure(state='disabled', fg_color="light gray")
            self.ebdbtype.configure(state='disabled', fg_color="light gray")
            self.ebdbgfilename.configure(state='disabled', fg_color="light gray")
            self.dbname.configure(text_color="gray40")
            self.dbip.configure(text_color="gray40")
            self.dbusername.configure(text_color="gray40")
            self.dbpassword.configure(text_color="gray40")
            self.dbdescription.configure(text_color="gray40")
            self.dbtype.configure(text_color="gray40")
            self.dbgfilename.configure(text_color="gray40")

            # Feedback to user on screen
            #self.lblcds.configure()
            self.lblcds.configure(text=" Device has been created. ", text_color="green4", bg_color="yellow2")
            #self.lblcds.configure()


            # Feedback to user by messagebox
            #msgboxname = str(self.ebdbname.get()) + " with IP address " + str(self.ebdbip.get())
            # messagebox.showinfo(title="New user created", message="Device " + str(msgboxname) + " has been created.")

            # Flush something to investigate ?
            #print(f"The value of self.trackingbool is :{self.trackingbool.get()}")
            clear_all(self)

        try:
            print("Create the directory structure here")
            devicepath = os.path.join(devicesaveddata, self.rname.get())
            if not os.path.exists(devicepath):
                os.mkdir(devicepath)
                print(f"4) New directory created (filesdb.py): {devicepath}")
        except Exception as error:
            print(f"ERROR (329) - {error}")
        finally:
            print("Running Finally")

# Change entry in the devices database
def changedbdevice(self, rdwindow, iam_selecteddevice):
    print(f"1 (changedbentry.py) changedbdevice : {iam_selecteddevice}")
    self.rdwindow.title("Update device details")
    self.rdwindow.geometry('500x740+200+200')
    self.rdwindow.attributes('-topmost', 'true')
    self.rdwindow.configure(fg_color="light blue")
    self.rdwindow.after(250, lambda: self.rdwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon)))))
    self.grid_rowconfigure(0, weight=1)
    self.grid_columnconfigure(0, weight=1)

    #self.result_id = IntVar(self)
    self.result_id=0
    #self.rname = StringVar(self, value="")
    self.changername = StringVar(self)
    #self.rip = StringVar(self, value="")
    self.changerip = StringVar(self)
    #self.rusername = StringVar(self, value="")
    self.changerusername = StringVar(self)
    self.changerpassword = StringVar(self)
    self.changerdescription = StringVar(self)
    self.changertype = StringVar(self)

    # self.rgfilename >(changedto)> self.rgfilename1
    self.rgfilename = tk.StringVar(self)

    self.createlabel = ctk.CTkLabel(self.rdwindow, text="Change device (id:", font=("arial", 22, "bold"))
    self.createlabel.grid(row=0, column=0,sticky="ew", columnspan=3, pady=(18, 8))
    self.separator1 = ttk.Separator(self.rdwindow, orient='horizontal')
    self.separator1.grid(row=1, sticky="ew", columnspan=3, padx=(30, 30), pady=10)
    self.createlabel.configure(text=self.createlabel.cget('text') + str(iam_selecteddevice) + ")")
    # device Name
    self.dbname = ctk.CTkLabel(self.rdwindow, text="Device name", font=("arial", 18, "bold"), justify='left')
    self.dbname.grid(row=2, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbname = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.changername) 
    self.ebdbname.grid(row=2, column=1, sticky='w', padx=(30, 30), pady=10)
    #self.ebdbname.insert(0, "Hello")
    # device IP Address
    self.dbip = ctk.CTkLabel(self.rdwindow, text="IP Address", font=("arial", 18, "bold"), justify='left')
    self.dbip.grid(row=3, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbip = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.changerip)
    self.ebdbip.grid(row=3, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Username
    self.dbusername = ctk.CTkLabel(self.rdwindow, text="Username", font=("arial", 18, "bold"), justify='left')
    self.dbusername.grid(row=4, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbusername = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.changerusername)
    self.ebdbusername.grid(row=4, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Password
    self.dbpassword = ctk.CTkLabel(self.rdwindow, text="Password", font=("arial", 18, "bold"), justify='left')
    self.dbpassword.grid(row=5, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbpassword = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.changerpassword)
    self.ebdbpassword.grid(row=5, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Description
    self.dbdescription = ctk.CTkLabel(self.rdwindow, text="Description", font=("arial", 18, "bold"), justify='left')
    self.dbdescription.grid(row=6, column=0, sticky='nw', padx=(30, 10), pady=10)
    self.ebdbdescription = ctk.CTkTextbox(self.rdwindow, font=("arial", 14), height=200, width=230, fg_color="light yellow", border_width=2, wrap='word') # self.changerdescription
    self.ebdbdescription.grid(row=6, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Type
    self.dbtype = ctk.CTkLabel(self.rdwindow, text="Type", font=("arial", 18, "bold"), justify='left')
    self.dbtype.grid(row=7, column=0, sticky='w', padx=(30, 10), pady=10)
    self.ebdbtype = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width= 230, fg_color="light yellow", textvariable=self.changertype)
    self.ebdbtype.grid(row=7, column=1, sticky='w', padx=(30, 30), pady=10)
    # device Goldern filename
    self.dbgfilename = ctk.CTkLabel(self.rdwindow, text="Golden config file", font=("arial", 18, "bold"), justify='left')
    self.dbgfilename.grid(row=8, column=0, sticky='ew', padx=(30, 10), pady=10)
    self.btnfile = ctk.CTkButton(self.rdwindow, text="File select", font=("arial", 14), text_color='white', fg_color="purple3", hover_color="purple4", border_color='purple4', border_width=2, command=lambda:filedialogopen(self))
    self.btnfile.grid(row=8, column=1, pady=10, padx=40)
    self.ebdbgfilename = ctk.CTkEntry(self.rdwindow, font=("arial", 14), justify='center', width=230, fg_color="light yellow", textvariable=self.rgfilename)
    self.ebdbgfilename.grid(row=9, column=0, sticky='ew', padx=(30, 30), pady=10, columnspan=2)
    self.ebdbgfilename.configure(state='disabled')
    
    self.separator2 = ttk.Separator(self.rdwindow, orient='horizontal')
    self.separator2.grid(row=10, sticky="ew", columnspan=3, padx=(30, 30), pady=10)
    self.btngo = ctk.CTkButton(self.rdwindow, text="Change", font=("arial", 14, "bold"), fg_color="green3", hover_color="green4", border_color='green4', border_width=2, text_color="black", command=lambda:dbconnectRegister(self, iam_selecteddevice))# 
    self.btngo.grid(row=11, column=0, pady=10, padx=(30, 10))
    self.btnexit = ctk.CTkButton(self.rdwindow, text="EXIT", font=("arial", 14, "bold"), fg_color="orange red", hover_color="OrangeRed3", text_color="black", border_width=2, border_color='OrangeRed3', command=self.rdwindow.destroy)
    self.btnexit.grid(row=11, column=1, pady=10, padx=(30, 10))
    self.lblcds = ctk.CTkLabel(self.rdwindow, text="", font=("arial", 18, "bold"), justify='left')# 
    self.lblcds.grid(row=12, column=0, pady=10, padx=(30, 10), columnspan=2)

    def filedialogopen(self):
        print(type(self.changername))
        basefiledirectory = f"{devicesaveddata}"+f"{self.changername.get()}"
        print(f"basefiledirectory: {devicesaveddata}"+f"{self.changername.get()}")
        # basefiledirectory = f"{devicesaveddata}"+"\\"+f"{self.changername.get()}"
        # print(f"basefiledirectory: {devicesaveddata}"+"\\"+f"{self.changername.get()}")
        getfile = askopenfilename(parent=self.rdwindow, title="Base File selection", initialdir=basefiledirectory)
        #self.rgfilename1.set(str(rgfilename1))
        #print(f"filedialogopen1 = {rgfilename1}")
        
        if len(getfile) == 0:
            print("No file selected")
            return
        
        filebase = os.path.basename(getfile)
        print(f"self.filebase = {filebase}")

        #print(f"self.rgfilename1.get() = {self.rgfilename1.get()}")
        #self.ebdbgfilename.insert(0, "newfilename")
        #global rgfilename
        self.rgfilename.set(filebase)


    try:
        sqliteConnection = sqlite3.connect(dbfile)
        cursor = sqliteConnection.cursor()
        #print("Successfull connection")
        query = ("SELECT * FROM devices WHERE id = '%s'" % (iam_selecteddevice))
        cursor.execute(query)
        results = cursor.fetchall()
        # sqliteConnection.commit()
        #print(f"Printing the entrytochange record {results}")
        self.result_id = results[0][0]
        #self.result_id.set(int(resid))
        #self.rname = results[0][1]
        self.changername.set(str(results[0][1]))
        #self.rip = results[0][2]
        self.changerip.set(str(results[0][2]))
        self.changerusername.set(str(results[0][3]))
        self.changerpassword.set(str(results[0][4]))
        self.changerdescription.set(str(results[0][5]))
        self.changertype.set(str(results[0][6]))
        self.rgfilename.set(str(results[0][7]))
        
        #print (f"results id =  {self.result_id}")
        #print (f"results name =  {self.rname}")
        #print (f"results ip =  {self.rip}")
        #print (f"results username =  {self.rusername}")
        #print (f"results password =  {self.rpassword}")
        #print (f"results description =  {self.rdescription}")
        #print (f"results type =  {self.rtype}")
        #print (f"results golden file =  {self.rgfilename}")

    except sqlite3.Error as error:
        self.lblcds.configure(text_color="red")
        self.lblcds.configure(text=f"Change request error: {error}")
        print("The error message is (line 452): ",error)
        print("Exception class is (line 453): ", error.__class__)
        print("Exception is (line 454):", error.args)

    # Insert the values into the entry boxes
    #self.ebdbname.insert(0, self.changername.get())
    #self.ebdbip.insert(0, self.rip)
    #self.ebdbip.insert(0, self.changerip.get())

    #self.ebdbusername.insert(0, self.rusername)
    #self.ebdbpassword.insert(0, self.rpassword)
    
    # Need because doesn't work like the rest of the inserts
    self.ebdbdescription.insert('1.0', self.changerdescription.get())
    
    #self.ebdbtype.insert(0, self.rtype)
    #self.ebdbgfilename.insert(0, self.rgfilename)

def dbconnectRegister(self, iam_selecteddevice):
    #print("Register button pressed")
    self.btngo.configure(state="disabled", fg_color="gray", border_color='gray')
    #print("id" + " = " + str(iam_selecteddevice))
    #print("Name" + " = " + str(self.ebdbname.get()))
    #print("IP" + " = " + str(self.ebdbip.get()))
    #print("Username" + " = " + str(self.ebdbusername.get()))
    #print("Password" + " = " + str(self.ebdbpassword.get()))
    #print("Description" + " = " + str(self.ebdbdescription.get('1.0','end-1c')))
    #print("Type" + " = " + str(self.ebdbtype.get()))
    #print("Golden config filename" + " = " + str(self.ebdbgfilename.get()))


    try:
        sqliteConnection = sqlite3.connect(dbfile)
        cursor = sqliteConnection.cursor()
        #print("Successfull connection")
        sqlite_select_Query = "select sqlite_version();"
        cursor.execute(sqlite_select_Query)
        record = cursor.fetchall()
        #print("SQLite Database Version is: ", record)
        query = ("UPDATE devices SET name = ?, ip = ?, username = ?, password = ?, description = ?, type = ?, golden = ? WHERE id = ?")
        inputs = (self.ebdbname.get(),self.ebdbip.get(),self.ebdbusername.get(),self.ebdbpassword.get(),self.ebdbdescription.get('1.0','end-1c'),self.ebdbtype.get(),self.ebdbgfilename.get(), self.result_id)
        cursor.execute(query, inputs)
        sqliteConnection.commit()

    except sqlite3.Error as error:
        print("The error message is (500): ",error)
        print("Exception class is (501): ", error.__class__)
        print("Exception is (502):", error.args)
           
    finally:
        if sqliteConnection:
            sqliteConnection.close()
            #msgboxname = str(self.rname.get()) + " with IP address " + str(self.rip.get())
            # self.ebdbname.delete(0, END)
            #self.ebdbip.delete(0, END)
            #self.ebdbusername.delete(0, END)
            #self.ebdbpassword.delete(0, END)
            #self.ebdbdescription.delete('1.0', END)
            #self.ebdbtype.delete(0, END)
            #self.ebdbgfilename.delete(0, END)

            # Change text and entry boxe to gray
            self.ebdbname.configure(state='disabled', fg_color="light gray")
            self.ebdbip.configure(state='disabled', fg_color="light gray")
            self.ebdbusername.configure(state='disabled', fg_color="light gray")
            self.ebdbpassword.configure(state='disabled', fg_color="light gray")
            self.ebdbdescription.configure(state='disabled', fg_color="light gray")
            self.ebdbtype.configure(state='disabled', fg_color="light gray")
            self.ebdbgfilename.configure(state='disabled', fg_color="light gray")
            self.dbname.configure(text_color="gray40")
            self.dbip.configure(text_color="gray40")
            self.dbusername.configure(text_color="gray40")
            self.dbpassword.configure(text_color="gray40")
            self.dbdescription.configure(text_color="gray40")
            self.dbtype.configure(text_color="gray40")
            self.dbgfilename.configure(text_color="gray40")
            self.btnfile.configure(state='disabled', fg_color="gray", border_color='gray')

            self.lblcds.configure(text=f" Device (id:{iam_selecteddevice}) has been changed. ", text_color="green4", bg_color="yellow2")

            # Redraw the device canvas
            clear_all(self)

def deletedbdevice(self, iam_selecteddevice):
    #print(f"I am the selected device id = {iam_selecteddevice}")
    self.deletemsgbox = messagebox.askquestion(title=f"Delete ? (id:{iam_selecteddevice})", message=f"Please confirm that you want to delete entry id:{iam_selecteddevice}", icon='warning')
    if self.deletemsgbox == 'yes':
        try:
            #print (f"I am deletedbdevice row selection is {iam_selecteddevice}")
            sqliteConnection = sqlite3.connect(dbfile)
            cursor = sqliteConnection.cursor()
            #print("Successfull connection")
            query = ("DELETE FROM devices WHERE id = '%s'") % (iam_selecteddevice)
            cursor.execute(query)
            sqliteConnection.commit()
            #print (f"RECORD DELETED deletedbdevice row selection is {iam_selecteddevice}")
            messagebox.showinfo(title='Return', message=f'The record with id:{iam_selecteddevice} was deleted.')
        except sqlite3.Error as error:
            messagebox.showinfo(title='Return', message=f'Problem occured: {error}')
            #print("The error message is: ",error)
            #print("Exception class is: ", error.__class__)
            #print("Exception is", error.args)
        finally:
            if sqliteConnection:
                sqliteConnection.close()
            
            # Redraw the device canvas
            clear_all(self)

    else:
        messagebox.showinfo(title='Request Canceled', message=f'You canceled the request.\n id:{iam_selecteddevice} remains in the list.')
'''
# Delete entry from the devices database
def deletedbdevice(self, iam_selecteddevice):
    #print(f"I am the selected device id = {iam_selecteddevice}")
    self.deletemsgbox = messagebox.askquestion(title=f"Delete ? (id:{iam_selecteddevice})", message=f"Please confirm that you want to delete entry id:{iam_selecteddevice}", icon='warning')
    if self.deletemsgbox == 'yes':
        try:
            #print (f"I am deletedbdevice row selection is {iam_selecteddevice}")
            sqliteConnection = sqlite3.connect(dbfile)
            cursor = sqliteConnection.cursor()
            #print("Successfull connection")
            query = ("DELETE FROM devices WHERE id = '%s'") % (iam_selecteddevice)
            cursor.execute(query)
            sqliteConnection.commit()
            #print (f"RECORD DELETED deletedbdevice row selection is {iam_selecteddevice}")
            messagebox.showinfo(title='Return', message=f'The record with id:{iam_selecteddevice} was deleted.')
        except sqlite3.Error as error:
            messagebox.showinfo(title='Return', message=f'Problem occured: {error}')
            #print("The error message is: ",error)
            #print("Exception class is: ", error.__class__)
            #print("Exception is", error.args)
        finally:
            if sqliteConnection:
                sqliteConnection.close()
            
            # Redraw the device canvas
            clear_all(self)

    else:
        messagebox.showinfo(title='Request Canceled', message=f'You canceled the request.\n id:{iam_selecteddevice} remains in the list.')

'''




devicecrud.py
jolders/devnetnode
from tkinter import *
from tkinter import ttk
import tkinter
from tkinter import messagebox
from tkinter import scrolledtext
import customtkinter as ctk
import mysql.connector
from mysql.connector import Error
from passlib.context import CryptContext
import sqlite3
from settings import *
import os
from os.path import exists
import shutil
import json
from checklistcombobox import *
from PIL import ImageTk, Image


def editthisgroup(self, *args, **kwargs):
    ewindow = tkinter.Toplevel()
    ewindow.title("Register")
    ewindow.geometry('600x820+200+200')
    ewindow.configure(background='powder blue')
    ewindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    groupname = StringVar(value="Your Group Name")
    
    # CONFIGURATION METHOD (Paramiko, netmiko etc.)
    configmethodvar = StringVar()
    
    # Member Select Combobar
    global memseleccbxvar
    memseleccbxvar = []

    # IDs of Members in the group
    global memidchkbxvar
    memidchkbxvar=[]
    global fvmemselec #floating ID's
    fvmemselec=[]

    # MeTHOD Select Combobar
    global methseleccbxvar
    methseleccbxvar = StringVar()

    memvaluesdb = []
    
    global cmdenryvar
    cmdenryvar = []

    print(f"1) This is edit group page the current working directory is : {os.getcwd()}")
    # os.chdir(directorypathroot)
    #  groupdevicepath = os.path.join(str(directorypathroot) + "\\devicegroups")
    os.chdir(devicegroups)
    items = os.listdir(devicegroups)
    for item in items:
        if os.path.isdir(item):
            memseleccbxvar.append(item)
            # print(f"Directories = {item}")
    print(f"2) memseleccbxvar value = {memseleccbxvar}")
    print(f"3) This is edit group page the current working directory is : {os.getcwd()}")

    def onexitcleanup():
        # cleanup all values in vars for next group ?
        print("I am the exit routine onexitcleanup (editgroups)")
        ewindow.destroy()
    
    def callchangegroupfile(self, *args, **kwargs):
        if grpnamebtn.cget('state') == DISABLED and memselecbtn.cget('state') == DISABLED and confbtn.cget('state')==DISABLED and grpdesbtn.cget('state')==DISABLED:
            print(f"grpnamebtn.cget('state') =  {grpnamebtn.cget('state')}")
            print(f"memselecbtn.cget('state') =  {memselecbtn.cget('state')}")
            print(f"confbtn.cget('state') =  {confbtn.cget('state')}")
            print(f"grpdesbtn.cget('state') =  {grpdesbtn.cget('state')}")
            changegroupfile(self, *args, **kwargs)
        else:
            #print("something isnt right a button is not in disabled state")
            #ewindow.attributes('-topmost',True)
            #ewindow.lift()
            messagebox.showinfo("Please Set changes", "Please 'Set' changes to confirm details", parent=ewindow)
            #ewindow.lift()
            #ewindow.after(2000, ewindow.lift())


    def changegroupfile(self, *args, **kwargs):
        print(f"1a) called by ''callchangegroupfile'' (changegroupfile)")
        print(f"1b) The current working directory is (changegroupfile): {os.getcwd()}")
    

        # HERE WE GO 
        print(f"GO (Group name: {groupname.get()})")
        print(f"GO (Member Selection: {fvmemselec})")
        print(f"GO (Config Method: {configmethodvar.get()})")
        print(f"GO (Commands: {cmdenryvar})")

        # Get into the right folder
        groudevicepath = os.path.join(devicegroups, groupname.get())
        # load the file
        dest_path = os.path.join(groudevicepath, groupname.get() + ".json")
        filetoread = open(dest_path)
        inputjsondata = json.load(filetoread)
        print(f"Addbutton filetoread = {filetoread}")
        print(f"Addbutton inputjsondata = {inputjsondata}")

        # change members selection
        inputjsondata['members'] = fvmemselec
        print(f"8) Data in members: {inputjsondata['members']}")

        # change config method
        try:
            # So I think that a value already exist
            inputjsondata['method'][0] = confmethodselec.get() # confmethodselec.get()
            print(f"9) Value already exits so used the replace method 3: {inputjsondata['method']}")
        except:
            print("10) An error occured so trying a second method")
            try:
                print(f"9) Value doesn't exits method 3: {inputjsondata['method']}")
                inputjsondata['method'] = confmethodselec.get()
            except Exception as e:
                print(f"9a) (105) THIS ERROR NEEDS LOOKING AT : DIDNT WORK EITHER 3a: {inputjsondata['method']}")
                print(f"9a) (106) ERROR 3a: {str(e)}")

        else:
            print("9b) (109) So the 1st try = Looks like this works")
                



        # change commands
        inputjsondata['commands'] = cmdenryvar
        #()
        print(f"10) Just want the first key commands: {inputjsondata['commands']}")

        # dump the file back
        print(f"11) I am 'inputjsondata' raw: {inputjsondata}")

        # OK Lets try try to change the data
        with open(dest_path, 'w', encoding='utf-8') as f:
            json.dump(inputjsondata, f, ensure_ascii=False, indent=4)

        # Close the file
        filetoread.close()

        btnadd.configure(state='disabled', fg_color='gray')

        editgrpstatuslbl.configure(text="Group Saved")

    def populatemembx(self, *args, **kwargs):
        print("Off to get the ID's to populate the checkbox selection")
        print("I'm going to fetch the list of IDs from the DB")
        try:
            sqliteConnection = sqlite3.connect(dbfile)
            cursor = sqliteConnection.cursor()
            query = (f"SELECT id FROM devices")
            cursor.execute(query)
            record = cursor.fetchall()
            result_list = [row[0] for row in record]
            result_list = list(map(str, result_list))
            print(result_list)
            memvaluesdb = result_list
            print(f"memvaluesdb = {memvaluesdb}")
            memselecchkbx.configure(values=memvaluesdb)

        
        except sqlite3.Error as error:
            print("The error (connecttodb.py(GETIDSFROMDB)) message is: ", error)
            print("Exception class is: ", error.__class__)
            print("Exception is", error.args)
        
    def grpcbxselc():
        global memselecchkbxvar
        global memseleccbxvar
        print("Set group profile name")
        groupname.set(grpnamecbx.get())
        print(f"1a) The current working directory is : {os.getcwd()}")
        print(f"2a) The groupname is : {groupname.get()}")
        groudevicepath = os.path.join(devicegroups, groupname.get())
        print(f"3a) The devicepath directory is : {str(groudevicepath)}")
        grpfilepath= os.path.join(groudevicepath, groupname.get() + ".json")
        print(f"4a) sourcepath = {grpfilepath}")
        grpfiletoread = open(grpfilepath)
        grpjsondata = json.load(grpfiletoread)
        print(f"1) I am 'inputjsondata' raw: {grpjsondata}")

        # # POPULATE THE MEMBERS IN THE GROUP SELECTION
        # THUS IS THE LIST OF ID's in the group
        print(f"7) Memberselection = {grpjsondata['members']}")
        memseleccbxvar = grpjsondata['members'] # Member Select Combobar
        print(f"8) Data in memseleccbxvar: {memseleccbxvar}")
        cgouttxtbx.configure(state=NORMAL)
        cgouttxtbx.insert(INSERT, f"1) Current Group is : {groupname.get()}\n")
        cgouttxtbx.insert(INSERT, f"2) Current Members (memseleccbxvar) are: {memseleccbxvar}\n")
        
        # THIS LINE POPULATES THE 'Member Selection' Combobox with the saved group values from members
        memselecchkbx.set(memseleccbxvar)
        #memidchkbxvar = memselecchkbx.get()
        populatemembx(self, *args, **kwargs)
        print(f"8a) Data in memseleccbxvar: {memseleccbxvar}")
        
        # POPULATE THE METHOD SELECTION
        # method = netmiko, parimamiko etc
        methseleccbxvar = grpjsondata['method']
        print(f"9) Just want the first key in method 3: {grpjsondata['method']}")
        print(f"10) Just want the methseleccbxvar in method 3: {methseleccbxvar}")
        cgouttxtbx.insert(INSERT, f"3) Current Method is : {methseleccbxvar}\n")
        confmethodselec.configure(state='normal')
        confmethodselec.set(methseleccbxvar)
        confmethodselec.configure(state='readonly')



        # POPULATE THE COMMNDS SELECTION
        cmdenryvar = grpjsondata['commands']
        #cmdenryvar =  cmdenryvar.splitlines()
        print(f"11) The commands are cmdenryvar: {cmdenryvar}")
        # Command to populate the commans box here
        print ("\n".join(cmdenryvar))
        grpdesetry.config(state=NORMAL)
        cmdenryvar1 = "\n".join(cmdenryvar)
        grpdesetry.insert(INSERT, cmdenryvar1)
        cgouttxtbx.insert(INSERT, f"4) Notes : {cmdenryvar}\n")

        grpnamecbx.configure(state=DISABLED, fg_color='gray70', text_color_disabled='gray26')
        grpnamebtn.configure(text="Accepted", fg_color='gray', state=DISABLED)

        # Close the file
        grpfiletoread.close()

        # Now activate the rest of the buttons
        #grpnamebtn.configure(state=DISABLED)
        #grpname.configure(state=DISABLED)
        memselecbtn.configure(state=NORMAL, fg_color='green' )
        memselecchkbx.configure(state='readonly')
        memselectlbl.configure(state=NORMAL)
        conflbl.configure(state=NORMAL)
        confbtn.configure(state=NORMAL, fg_color='green')
        confmethodselec.configure(state='readonly', fg_color="pale green")
        grpdesbtn.configure(state=NORMAL, fg_color='green')
        grpdesetry.configure(state=NORMAL)
        grpdesclbl.configure(state=NORMAL)
        grpdesetry.configure(background="light yellow")
        
        btnadd.configure(state=NORMAL, fg_color='green')
        
    def memselectfun():
        global memidchkbxvar
        global fvmemselec
        memidchkbxvar = memselecchkbx.get()
        fvmemselec = []
        # Get the current combobox selected values
        if memselecchkbx.get() == '':
            print("memselecchkbx.get() is null")
            print(f"So use values: {memseleccbxvar}")
            cgouttxtbx.insert(INSERT, f"2) Remaines Unchanged Current Members: {memseleccbxvar}\n")
            fvmemselec = memseleccbxvar
        else:
            print("memselecchkbx.get() is not null")
            print(f"So use values: {memselecchkbx.get()}")
            cgouttxtbx.insert(INSERT, f"2) Changing Group Current Members to: {memselecchkbx.get()}\n")
            fvmemselec = memselecchkbx.get()
        
        # USE THIS VALUE TO SAVE TO THE FILE "fvmemselec"
        print(f"The floating variable for fvmemselec {fvmemselec}")
        
        #DISABLE THE MEMBER SECTION LINE
        memselectlbl.configure(state=DISABLED)
        memselecchkbx.configure(state=DISABLED) # Background Gray
        memselecbtn.configure(state=DISABLED, fg_color="gray", text="Accepted")
        #memselecbtn.configure()


    def confmethodselect():
        print("I am confmethodselect")
        cgouttxtbx.configure(state=NORMAL)
        cgouttxtbx.insert(INSERT, f"3) Config Method: {confmethodselec.get()}\n")
        cgouttxtbx.configure(state=DISABLED)
        configmethodvar.set(confmethodselec.get())
        print(f"configmethodvar value is : {configmethodvar.get()}")
        
        # USE configmethodvar AS VALUE
        # DESELECT LABEL, COMBOBOX , BUTTON
        conflbl.configure(state=DISABLED)
        confmethodselec.configure(state=DISABLED, fg_color='gray70', text_color_disabled='gray26')
        confbtn.configure(state=DISABLED, text="Accepted", fg_color="gray")


    def grpcmdstxtbx():
        global cmdenryvar
        print("I am grpcmdstxtbx")
        inp = grpdesetry.get(1.0, "end-1c")
        cgouttxtbx.config(state=NORMAL)
        cgouttxtbx.insert(INSERT, "4) Commands: " + "\n"+inp)
        cgouttxtbx.config(state=DISABLED)
        cmdenryvar =  inp.splitlines()
        print(f"inp={inp}")
        print(f"cmdenryvar={cmdenryvar}")
        #cgouttxtbx.insert(INSERT, cmdenryvar)

        # DESELECT LABEL, TEXTBOX , BUTTON
        grpdesetry.configure(bg="gray70")
        grpdesclbl.configure(state=DISABLED)
        grpdesetry.configure(state=DISABLED)
        grpdesbtn.configure(state=DISABLED, fg_color='gray', text="Accepted")

        #########################################################
        # IF BUTTON MEMBER AND BUTTON CONFIG AND NOTES BUTTON = DISABLED ACTIVATE SET CHANGES OPTION
        #########################################################

######################## none of this styling works
    style = ttk.Style()
    style.theme_use("clam")
    self.option_add("*TCombobox*Listbox*Background", 'pale green') # Background of dropdown selectbox
    self.option_add('*TCombobox*Listbox*Foreground', 'black') # Text colour of items in dropdown
    self.option_add('*TCombobox*Listbox*selectBackground', 'PaleGreen4') # Highlight selection
    self.option_add('*TCombobox*Listbox*selectForeground', 'white') # Test Highlight selection on dropdown
    self.option_add("*TCombobox*Listbox*Font", 'Helvetica 10 bold')

    style.map('TCombobox', fieldbackground=[('readonly','PaleGreen2')]) # field background empty
    style.map('TCombobox', selectbackground=[('readonly', 'PaleGreen2')]) # field background being filled bu numbers
    style.map('TCombobox', selectforeground=[('readonly', 'black')])
    style.map('TCombobox', selectbackground=[('disabled', 'gray20')])


    ewindow.grid_columnconfigure(0, weight=1)
    # device name
    createlabel = ctk.CTkLabel(ewindow, text="Edit a device group", font=('arial', 22, 'bold'))
    createlabel.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(20,16))
    separator1 = ttk.Separator(ewindow, orient='horizontal')
    separator1.grid(row=1, sticky=EW, columnspan=3, padx=20, pady=10)
    
    # GROUP NAME (Group1, London Routers, WestCoastSwitches)
    grpname = ctk.CTkLabel(ewindow, text="Group name", font=('arial', 18, 'bold'))
    grpname.grid(row=2, column=0,sticky=W, padx=25, pady=10)
    grpnamecbx = ctk.CTkComboBox(ewindow, values=memseleccbxvar,  font=('arial', 14, 'bold'), fg_color="pale green")
    grpnamecbx.grid(row=2, column=1,sticky=EW, padx=10, pady=10)
    grpnamecbx.set("select Group to edit")
    grpnamebtn = ctk.CTkButton(ewindow, text="Set", command=grpcbxselc, width=100, fg_color="green", hover_color='green4')
    grpnamebtn.grid(row=2, column=2, padx=30, pady=10)


    # MEMBER SELECTION
    memselectlbl = Label(ewindow, text="Member Selection", font='arial 14 bold', bg="powder blue", fg='black', state=DISABLED)
    memselectlbl.grid(row=3, column=0 ,sticky=W, padx=25, pady=10)
    memselecchkbx = ChecklistCombobox(ewindow, values=memvaluesdb, font=('arial 12 bold'), state='disabled', checkbutton_height=3, height=12, style='TCombobox')
    memselecchkbx.grid(row=3, column=1,sticky=EW, padx=10, pady=10)
    memselecbtn = ctk.CTkButton(ewindow, text="Set", command=memselectfun, state=DISABLED, width=100, fg_color="gray", hover_color='green4')
    memselecbtn.grid(row=3, column=2, padx=30, pady=10)
    
    # CONFIGURATION COMBOBOX SELECTION
    conflbl = Label(ewindow, text="Config Method", font='arial 14 bold', bg='powder blue', fg='black', state=DISABLED)
    conflbl.grid(row=4, column=0,sticky=W, padx=25, pady=10)
    #confoptions = ['Paramiko', 'Netmiko', 'Netmiko_ch', 'Netmiko_ch_config', 'other-3']
    confmethodselec = ctk.CTkComboBox(ewindow, values=self.cmdcomboboxoptions, state='disabled', font=('arial', 14, 'bold'))
    confmethodselec.grid(row=4, column=1,sticky=EW, padx=10, pady=10)
    confbtn = ctk.CTkButton(ewindow, text="Set", command=confmethodselect, state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    confbtn.grid(row=4, column=2, padx=30, pady=10)   

    # device description
    grpdesclbl = Label(ewindow, text="Notes", font='arial 14 bold', bg='powder blue', fg='black', state=DISABLED)
    grpdesclbl.grid(row=5, column=0,sticky=NW, padx=25, pady=10)
    
    #grpdesetry = Text(ewindow, font='arial 14', height = 10, width = 25, bg="light yellow", wrap=WORD)
    grpdesetry = scrolledtext.ScrolledText(ewindow, font='arial 12', height = 10, width = 25, bg="gray70", wrap=WORD, state=DISABLED)
    grpdesetry.grid(row=6, column=0, sticky=EW, padx=(25, 10), pady=10, columnspan=2)
    grpdesbtn = ctk.CTkButton(ewindow, text="Set", command=grpcmdstxtbx, state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'))
    grpdesbtn.grid(row=6, column=2, sticky=S, padx=30, pady=10) 


    separator2 = ttk.Separator(ewindow, orient='horizontal')
    separator2.grid(row=7, sticky=EW, columnspan=3, padx=20, pady=10)
    btnadd = ctk.CTkButton(ewindow, text="Set changes", state=DISABLED, width=100, fg_color="gray", hover_color='green4', font=('arial', 12, 'bold'), command=lambda:callchangegroupfile(self, *args, **kwargs))
    btnadd.grid(column=0, row=8, pady=10)
    btnexit = ctk.CTkButton(ewindow, text="EXIT", width=100, fg_color="red2", hover_color='red3', font=('arial', 12, 'bold'), command=onexitcleanup)
    btnexit.grid(column=1, row=8, pady=10, padx=25)
    cgouttxtbx = scrolledtext.ScrolledText(ewindow, font='arial 10', height = 10, width = 25, bg="PaleGreen2", wrap=WORD, state=DISABLED)
    cgouttxtbx.grid(row=9, column=0,sticky=EW, padx=(25, 25), pady=10, columnspan=3)

    editgrpstatuslbl = Label(ewindow, text="", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    editgrpstatuslbl.grid(row=10, column=0, sticky=W, padx=(30, 20), pady=10, columnspan=3)


    if grpnamebtn.cget("state") == DISABLED:
        print("grpnamebtn(state='disabled')")

editgroups.py
jolders/devnetnode
# processes = psutil / pip install psutil
# pathlib # purepath / home / cwd exists rename
from pprint import pprint
import psutil
import os
from os.path import exists
import shutil # Copying Moving Deleting
from tkinter import *
from tkinter import scrolledtext
# import tkinter as tk
from settings import *
import pathlib # filesystem paths
from connecttodb import getdevicefromid
import sqlite3
from mysql.connector import Error
from datetime import datetime


def saveframeoutput(self):
    # If grp checkbox = false use : thisdevicename = self.returnedname.get()
    # If grp checkbox = true use : thisdevicename = self.grpsectselect.get()
    thisdevicename=""
    saveoutputroot=""
    
    if self.groupswitch_var.get() == "on":
        thisdevicename = f"{self.groupoptionselected.get()}"
        print(f"ON=self.groupswitch_var on/off value")
        print(f"ON=self.groupswitch_var thisdevicename: {thisdevicename}")
        saveoutputroot = devicegroups # f"/Shared/NetDevApp3/cisco12ct/devicegroups/"
        print(f"ON=self.groupswitch_var saveoutputroot : {saveoutputroot}")
    else:
        print(f"OFF=self.groupswitch_var value: {self.groupswitch_var.get()}")
        thisdevicename = f"{self.returnedname.get()}"
        print(f"OFF=self.groupswitch_var off value")
        print(f"OFF=self.groupswitch_var thisdevicename: {thisdevicename}")
        saveoutputroot = devicesaveddata # f"/Shared/NetDevApp3/cisco12ct/devicedata/"
        print(f"OFF=self.groupswitch_var saveoutputroot : {saveoutputroot}")
                                                                    

    print(f"1) self.groupswitch_var on/off value (filesdb.py): {str(self.groupswitch_var.get())}")
    print(f"1) self.groupoptionselected.get() = (filesdb.py)=: {str(self.groupoptionselected.get())}")
    print(f"1) self.groupoptionselected.get() = (filesdb.py)=: {str(self.returnedname.get())}")
    print(f"1) saveoutput thisdevicename (filesdb.py): {thisdevicename}")
    
    #Create a folder
    print(f"2) The current working directory is (filesdb.py): {os.getcwd()}")
    print(f"2) devicesaveddata: {devicesaveddata}")
    print(f"2) thisdevicename : {thisdevicename}")
    devicepath = os.path.join(saveoutputroot, thisdevicename)
    print(f"2) The devicepath directory is (filesdb.py): {devicepath}")
    
    if not os.path.exists(devicepath):
        os.mkdir(devicepath)
        print(f"4) New directory created (filesdb.py): {devicepath}")
    
    # change directory to current directory
    os.chdir(os.path.join(saveoutputroot, thisdevicename))
    print(f"5) Directory path root is now (filesdb.py): {os.getcwd()}")
    print(f"5) devicesaveddata: {devicesaveddata}")
    print(f"5) thisdevicename (thisdevicename): {devicesaveddata}")
    
    
    mydatetime = datetime.now().strftime("%Y-%m-%d_%H-%M-%S"+".txt")
    print(f"6) (filesdb.py) mydatetime: {mydatetime}")
    
    '''
    if exists(myfilename):
        print("The file already exists")
    else:
        print("The file does not exist")
        try:
            print("START - create file")
            second = open(myfilename, 'x')
            print("END - create file")
        except:
            print("A problem creating a base file")
    '''
    
            
    # Capture data in the window
    stufftowrite = self.txtcontent.get("1.0", END)
    print(f"7) This is stuff captured from txtcontent (filesdb.py): {stufftowrite}")
    
    
            
    appenddata = open(mydatetime, 'w', encoding="utf-8")
    appenddata.write(stufftowrite)
    appenddata.close()
    
    #return base dir to base
    os.chdir(directorypathroot)
    print(f"8) The current working directory is (filesdb.py) : {os.getcwd()}")

filesdb.py
jolders/devnetnode
from settings import *
import customtkinter as ctk
import tkinter as tk
from tkinter import ttk, scrolledtext
from tkinter import *
from PIL import ImageTk, Image
from devicecrud import fetchalldb, createdevice, changedbdevice, deletedbdevice
from connecttodb import getdevicefromid
from cmdframebox import showcmdframe
from filesdb import saveframeoutput
from sshservices import runcmdparamiko, runcmdnetmiko, runcmdnetmiko_ch, runcmdnetmiko_ch_config
from sshservicesgrp import runcmdparamikogrp, runcmdnetmikogrp, runcmdnetmikogrp_ch
from creategroup import createnewgroup
import os
from editgroups import editthisgroup
from deletegroup import delgroup
from backuptftp import tftpdevicebackup # tftpsaveconfig,
from backuptftpgrp import backupgrouptftp # tftpsaveconfiggrp
from uploadtftp import configuploadtftp
from uploadtftpgroup import configuploadtftpgroup

class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        ## Setting up Initial Things
        self.title("DevNetNode")
        self.geometry("1400x750+50+50")
        self.resizable(True, True)
        self.minsize(1100,550)
        self.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))
        # self.iconbitmap('/Shared/NetDevApp3/cisco12ct/pythonicon.ico')

        # The selected device on the treeview
        self.iam_selecteddevice = tk.IntVar(self)
        # Used in Status bar with a value like Device ID or Group ID
        self.devicestatus = tk.StringVar(self, value=f"Device ID: ")
        # Status bar with a value like 5(selected index) or RTR1 (selected group)
        self.statusvargrp = tk.StringVar(self)
        # Group switch variable on self.topframea
        self.groupswitch_var = tk.StringVar(value="off")
        self.statusvarcmd = tk.StringVar(self, value="(Unselected)") # StatusLabel 'Paramiko', 'Netmiko', 'Netmiko_ch', 'Netmiko_ch_config', 'other-3'
        self.statusvarcmd2 = tk.StringVar(self, value="(Unselected)") # # StatusLabel 'sh ip int bri'

        self.groupcomboboxoptions = [] # 'Group1', 'Group2', 'Group3', 'Group4', 'Group5'
        self.groupoptionselected = tk.StringVar(self)

        self.cmdcomboboxoptions = ['Paramiko', 'Netmiko', 'Netmiko_ch', 'Netmiko_ch_config', 'Another ?']
        self.cmdmethodselected = tk.StringVar(self) # Netmiko

        self.cmdvarselected = tk.StringVar(self) # show interface gigabitEthernet 0/0

        self.returnedname = tk.StringVar(self) # Floating variable with hostname properties (RTR1)


        def createnewdevice(self): 
            self.cdwindow = ctk.CTkToplevel()
            # self.cdwindow.grab_set() # Problems on linux
            createdevice(self, self.cdwindow)

        def changethisdevice(self):
            print(f"Printing changethisdevice function={self.iam_selecteddevice.get()}")
            self.rdwindow = ctk.CTkToplevel()
            # self.rdwindow.grab_set() # Problems on linux
            changedbdevice(self, self.rdwindow, self.iam_selecteddevice.get())

        def deletethisdevice(self):
            print(f"Printing deletethisdevice function={self.iam_selecteddevice.get()}")
            deletedbdevice(self, self.iam_selecteddevice.get())

        def printstuff(self):
            print(f"1) printstuff self.iam_selecteddevice = {self.iam_selecteddevice.get()}")
            print(f"2) printstuff self.statusvargrp = {self.statusvargrp.get()}")
            print(f"3) printstuff self.groupoptionselected = {self.groupoptionselected.get()}")
            print(f"3) printstuff self.cmdmethodselected = {self.cmdmethodselected.get()}")

        def groupswitch_event(self):
            #print(f"Groupswitch_event value is :{self.groupswitch_var.get()}")
            #self.statusvargrp.set(self.groupswitch_var.get())
            self.groupcomboboxoptions = []
            listgroupnames(self)
            
            if self.groupswitch_var.get() == "on":
                self.devicestatus.set("Selected Group : ")
                self.statusvargrp.set(self.grpcombobox.get())
                self.iam_selecteddevice.set(0)
                # work needed to disable the frame showing the tree view database of devices
                self.grpcombobox.configure(state='readonly')
                self.grpcombobox.configure(fg_color='yellow2')
                self.grpcombobox.configure(values=self.groupcomboboxoptions)
                self.groupoptionselected.set(self.grpcombobox.get()) # put combobox value into var
                self.createa.configure(state='disabled')
                self.updatea.configure(state='disabled')
                self.deletea.configure(state='disabled')
                
                # Makes selection in tree view inactive and look inactive
                self.tree.bind('<Button-1>', 'break') 
                self.tree.bind('<MouseWheel>', 'break')
                self.vsb.config(command="")
                style = ttk.Style()
                style.configure("db.Treeview", background="gray64", relief="flat", rowheight="25")
                style.configure("db.Treeview.Heading", background="gray64", foreground="black", font=('Helvetica 10 bold'))
                style.map("Treeview", background=[('selected', "gray45")])
                self.tree.bind("<Button-3>", 'break')

            else:
                print("switch value is false")
                self.devicestatus.set(f"Device ID: ")
                self.grpcombobox.configure(state='disabled')
                self.grpcombobox.configure(fg_color='light goldenrod yellow')
                #self.grpcombobox.set('') #THIS NEEDS WORK ON
                self.iam_selecteddevice.set(self.tree.item(self.tree.selection())['values'][0])
                #self.cmdmethodselected.set('') self.groupoptionselected
                self.createa.configure(state='normal')
                self.updatea.configure(state='normal')
                self.deletea.configure(state='normal')
                
                # Makes selection in tree view active and look active
                self.tree.unbind('<Button-1>') 
                self.tree.unbind('<MouseWheel>')
                self.vsb.config(command=self.tree.yview)
                
                style = ttk.Style()
                style.theme_use("clam")
                style.configure("db.Treeview", background="pale green", relief="flat", rowheight="25")
                style.configure("db.Treeview.Heading", background="PaleGreen3", foreground="black", font=('Helvetica 10 bold'))
                style.map("Treeview", background=[('selected', "PaleGreen4")])
                
                self.option_add("*TCombobox*Listbox*Background", 'pale green') # Background of dropdown selectbox
                self.option_add('*TCombobox*Listbox*Foreground', 'black') # Text colour of items in dropdown
                self.option_add('*TCombobox*Listbox*selectBackground', 'PaleGreen4') # Highlight selection
                self.option_add('*TCombobox*Listbox*selectForeground', 'white') # Test Highlight selection on dropdown
                self.option_add("*TCombobox*Listbox*Font", 'Helvetica 10 bold')

                style.map('TCombobox', fieldbackground=[('readonly','PaleGreen2')]) # field background empty
                style.map('TCombobox', selectbackground=[('readonly', 'PaleGreen2')]) # field background being filled bu numbers
                style.map('TCombobox', selectforeground=[('readonly', 'black')]) # Text numbers 5,7,9
                #style.map('TCombobox', fieldbackground=[('disabled','gray20')]) # field background empty
                style.map('TCombobox', selectbackground=[('disabled', 'gray20')]) # field background being filled bu numbers
                #style.map('mcbx.TCombobox', selectbackground=[('disabled', 'gray20')])
        
        def runcmdselection(self):
            print(f"The value of varselected is: {self.iam_selecteddevice.get()}")
            #self.statuslblcmd.set("Connecting.......(runcmdselection)") # doesnt feedback in a timely manner
            #print(f"3) runcmdselection self.statusvargrp = {self.statusvargrp.get()}")
            
            if self.groupswitch_var.get() == 'off':
                print(f"1) runcmdselection self.groupswitch_var (OFF) = {self.groupswitch_var.get()}")
                print(f"2) runcmdselection self.iam_selecteddevice (#VALUE) = {self.iam_selecteddevice.get()}")
                print(f"3) runcmdselection self.cmdmethodselected = {self.cmdmethodselected.get()}")
                print(f"4) runcmdselection self.cmdvarselected = {self.cmdvarselected.get()}")
                print(f"Printing load device def iam_selecteddevice = {self.iam_selecteddevice.get()}'")
                # pass required vars to sshservices
                if self.cmdmethodselected.get() == "Paramiko":
                    print (f"Paramiko will be used to run '{self.iam_selecteddevice.get()}' ")
                    runcmdparamiko(self, self.iam_selecteddevice.get())
                if self.cmdmethodselected.get() == "Netmiko":
                    print (f"Netmiko will will be used to run '{self.iam_selecteddevice.get()}' ")
                    runcmdnetmiko(self, self.iam_selecteddevice.get())
                if self.cmdmethodselected.get() =="Netmiko_ch":
                    print (f"Netmiko.connectionHandler will will be used to run '{self.iam_selecteddevice.get()}' ")
                    runcmdnetmiko_ch(self, self.iam_selecteddevice.get())
                if self.cmdmethodselected.get() == "Netmiko_ch_config":
                    # set commands are not relevant so allow input into textbox
                    print (f"Netmiko_ch_config will will be used to run '{self.iam_selecteddevice.get()}' ")
                    runcmdnetmiko_ch_config(self, self.iam_selecteddevice.get())
                

            elif self.groupswitch_var.get() == 'on':
                print(f"1) runcmdselection self.groupswitch_var (ON) = {self.groupswitch_var.get()}")
                print(f"2) runcmdselection self.iam_selecteddevice (#VALUE=0) = {self.iam_selecteddevice.get()}")
                print(f"2) The Group value is  (self.groupoptionselected) = {self.groupoptionselected.get()}")
                print(f"3) runcmdselection self.cmdmethodselected = {self.cmdmethodselected.get()}")
                print(f"4) runcmdselection self.cmdvarselected = {self.cmdvarselected.get()}")
                #self.statustext.set(self.varselected.get())
                if self.cmdmethodselected.get() == "Paramiko":
                    print (f"Paramiko 'grp' will be used to run on group: '{self.groupoptionselected.get()}' ")
                    print (f"Paramiko 'grp' runcmdparamikogrp (self.groupoptionselected.get())= '{self.groupoptionselected.get()}' ")
                    print (f"Paramiko 'grp' runcmdparamikogrp (self.cmdvarselected.get())= '{self.cmdvarselected.get()}' ")
                    runcmdparamikogrp(self, self.groupoptionselected.get(), self.cmdvarselected.get()) # sshservicesgrp.py
                if self.cmdmethodselected.get() == "Netmiko":
                    print (f"Netmiko 'grp' will will be used to run '{self.groupoptionselected.get()}' ")
                    runcmdnetmikogrp(self, self.groupoptionselected.get(), self.cmdvarselected.get())
                if self.cmdmethodselected.get() == "Netmiko_ch": # Netmiko with 'Connection Handler'
                    print (f"Netmiko_ch 'grp' will will be used to run '{self.groupoptionselected.get()}' ")
                    runcmdnetmikogrp_ch(self, self.groupoptionselected.get(), self.cmdvarselected.get())
                if self.cmdmethodselected.get() == "Netmiko_ch_config": # Netmiko with 'Connection Handler'
                    print (f"Netmiko_ch_config 'grp' will will be used to run '{self.groupoptionselected.get()}' ")
                    runcmdnetmikogrp_ch(self, self.groupoptionselected.get()) # , self.cmdvarselected.get()

            else:
                print("runcmdselection self.groupswitch_var = UNDEFINED")
     
        def newgroup():
            print("You have accessed 'newgroup' function")
            createnewgroup(self, *args, **kwargs)
        def editgroup():
            print("You have accessed 'editgroup' function")
            editthisgroup(self, *args, **kwargs)
        def deletegroup():
            print("You have accessed 'deletegroup' function")
            delgroup(self)
      
        def listgroupnames(self):
            os.chdir(directorypathroot)
            print(f"2) (listgroupnames(self)) The current working directory is : {os.getcwd()}")
            # groupdevicepath = os.path.join(str(directorypathroot) + "\\devicegroups")
            os.chdir(devicegroups)
            print(f"3) (listgroupnames(self)) The current working directory is : {os.getcwd()}")
            
            items = os.listdir(devicegroups)
            for item in items:
                if os.path.isdir(item):
                    self.groupcomboboxoptions.append(item)
            print(f"4) Directories (groupcomboboxoptions) = {self.groupcomboboxoptions}")
            print(f"5) (listgroupnames(self)) The current working directory is : {devicegroups}")

        def groupcombobox_callback(choice):
            print("groupcombobox dropdown clicked:", choice)
            self.groupoptionselected.set(choice)
            self.statusvargrp.set(choice)
        def cmdcombobox_callback(choice):
            print("cmdcombobox dropdown clicked:", choice)
            print("This is being used intead of show_frame")
            self.cmdmethodselected.set(choice)
            self.statusvarcmd.set(choice)
            showcmdframe(self, choice, *args, **kwargs)

        def tftpconfig(): # self, *args, **kwargs ? > backuptftp.py
            # repeat main.py(231) devicecrud.py(111)
            print(f"tftpconfig {self.iam_selecteddevice.get()}") #backupftp.py
            #tftpsaveconfig(self, self.iam_selecteddevice.get())
            tftpdevicebackup(self)
        
        def tftpconfiggrp(): # self, *args, **kwargs
            # backupftpgrp.py
            print("This is the tftpconfiggrp function in main")
            backupgrouptftp(self)
            
            '''
            if self.groupswitch.get() == 'on' and self.groupoptionselected.get() != "Group Selection":
                #print("Looks like you're good to go")
                print(f"Group selection = {self.groupoptionselected.get()}")
                tftpsaveconfiggrp(self, self.groupoptionselected.get())
            else:
                print("Looks like you've not selected to checkbox and group")
            '''
            
        
        def tftupload(): #self, *args, **kwargs ?
            print("I am tftupload")
            configuploadtftp(self) #uploadtftp.py
        
        def tftprestoregroup():
            print("I am tftprestoregroup")
            configuploadtftpgroup(self)


        listgroupnames(self)

        # ------ Menu Start ------
        myMenu1 = Menu(self)
        self.config(menu=myMenu1)
        
        fileMenu = Menu(myMenu1, tearoff=0)
        myMenu1.add_cascade(label="File", menu=fileMenu)
        fileMenu.add_separator()
        fileMenu.add_command(label="Exit", command=self.quit)

        deviceMenu = Menu(myMenu1, tearoff=0)
        myMenu1.add_cascade(label="Devices", menu=deviceMenu)
        deviceMenu.add_command(label="Create a new Device", command=lambda:createnewdevice(self))
        deviceMenu.add_separator()
        deviceMenu.add_command(label="Edit an exsisting Device", command=lambda:changethisdevice(self))
        deviceMenu.add_command(label="Delete selected Device", command=lambda:deletethisdevice(self))
        
        grpMenu = Menu(myMenu1, tearoff=0)
        myMenu1.add_cascade(label="Device Groups", menu=grpMenu)
        grpMenu.add_command(label="Create a new Group", command=newgroup)
        grpMenu.add_separator()
        grpMenu.add_command(label="Edit an exsisting Group", command=editgroup)
        grpMenu.add_command(label="Delete a Group", command=deletegroup)

        tftpMenu = Menu(myMenu1, tearoff=0)
        myMenu1.add_cascade(label="TFTP Backup & Restore", menu=tftpMenu)
        tftpMenu.add_command(label="Backup Device (TFTP)", command=tftpconfig)
        tftpMenu.add_command(label="Restore Device (TFTP)", command=tftupload)
        tftpMenu.add_separator()
        tftpMenu.add_command(label="Backup Group (TFTP)", command=tftpconfiggrp)
        tftpMenu.add_command(label="Restore Group (TFTP)", command=tftprestoregroup)
        
        
        versionMenu = Menu(myMenu1, tearoff=0)
        myMenu1.add_cascade(label="                                                                                                                                         Version 0.03 (beta)", menu=versionMenu)
        # ------ Menu END ------

        def topFrame(self):
            self.topframemain = ctk.CTkFrame(self, height=40)
            self.topframemain.pack(fill='x', side='top') # expand=TRUE
            self.topframemain.pack_propagate(False)

            self.topframea = ctk.CTkFrame(self.topframemain, height=40, fg_color="PaleGreen3")
            self.topframea.pack(fill='x', expand=True, side='left')
            self.topframea.pack_propagate(False)

            self.topframeb = ctk.CTkFrame(self.topframemain, height=40, fg_color="lightblue")
            self.topframeb.pack(fill='x', expand=True, side='left')
            self.topframeb.pack_propagate(False)

            # Label Placeholders
            #self.lbltopframea = ctk.CTkLabel(self.topframea, text="topframea").pack(side='left')
            #self.btntopframea = ctk.CTkButton(self.topframea, text="btntopframea", command=lambda:printvalues()).pack(side='left')
            #self.lbltopframeb = ctk.CTkLabel(self.topframeb, text="topframeb").pack()
            self.createa = ctk.CTkButton(self.topframea, text='Create', width=75, command=lambda:createnewdevice(self), fg_color='dark slate gray', 
                                         font=('arial', 12, 'bold'), hover_color='Gray24', border_width=3, border_color='dark slate gray')
            self.createa.pack(side='left', padx=5)
            self.updatea= ctk.CTkButton(self.topframea, text='Update', width=75, command=lambda:changethisdevice(self), fg_color='dark slate gray', 
                                        font=('arial', 12, 'bold'), hover_color='Gray24', border_width=3, border_color='dark slate gray')
            self.updatea.pack(side='left', padx=5)
            self.deletea = ctk.CTkButton(self.topframea, text='Delete', width=75, command=lambda:deletethisdevice(self), fg_color='dark slate gray', 
                                         font=('arial', 12, 'bold'), hover_color='Gray24', border_width=3, border_color='dark slate gray')
            self.deletea.pack(side='left', padx=5)

            # PRINTVALUE
            # self.printvalues = ctk.CTkButton(self.topframea, text='PRINTVALUE', width=25, command=lambda:printstuff(self))
            # self.printvalues.pack(side='left', padx=5)

            self.grpcombobox = ctk.CTkComboBox(self.topframea , width=170, height=20, font=('arial', 14, 'bold'), command=groupcombobox_callback, dropdown_fg_color='khaki', 
                                               dropdown_hover_color='khaki3', border_color='dark slate gray', button_color='dark slate gray')
            self.grpcombobox.pack(side='right', pady=5, padx=10) # values=self.cmdcomboboxoptions,
            self.grpcombobox.set("Group Selection")
            self.grpcombobox.configure(state='disabled')

            # Group switch on/off
            self.groupswitch = ctk.CTkSwitch(self.topframea, text="Group Switch", command=lambda:groupswitch_event(self), variable=self.groupswitch_var, 
                                             onvalue="on", offvalue="off", switch_width=40, switch_height=20, fg_color='khaki2', 
                                             button_color='dark slate gray', border_width=2, border_color='dark slate gray', progress_color='yellow')
            self.groupswitch.pack(side='right', pady=10, padx=(10,10))

            self.lblcmd = ctk.CTkLabel(self.topframeb, text=" CMD select: ").pack(side='left', padx=10)

            self.cmdcombobox = ctk.CTkComboBox(self.topframeb, width=170, height=20, values=self.cmdcomboboxoptions, font=('arial', 14, 'bold'), fg_color='yellow2', command=cmdcombobox_callback, dropdown_fg_color='khaki', 
                                               dropdown_hover_color='khaki3', border_color='dark slate gray', button_color='dark slate gray', button_hover_color='dark green') #, command=cmdcombobox_callback
            self.cmdcombobox.pack(side='left', pady=5, padx=10)
            self.cmdcombobox.set("Method Selection")
            # self.cmdcombobox.configure(state='disabled')

            def copy_select(): # copy selected text to clipboard
                global cmddata
                if self.cmdtxtbox.selection_get():
                    cmddata=self.cmdtxtbox.selection_get() # copy selected text to clipboard
                    print(f"Copy ={cmddata}")
                    self.clipboard_clear()
                    self.clipboard_append(cmddata)
            
            def paste_select():
                global data
                self.cmdtxtbox.insert(tk.END,self.clipboard_get()) # Paste data from clipboard

            self.btncopy = ctk.CTkButton(self.topframeb,text='Paste', width=30, command=lambda:paste_select()) # ,command=lambda:copy_select()
            self.btncopy.pack(side='right', padx=5)
            self.btnpaste = ctk.CTkButton(self.topframeb,text='Copy', width=30, command=lambda:copy_select()) # ,command=lambda:paste_select()
            self.btnpaste.pack(side='right', padx=5)
            # On startup disable the buttons
            self.btncopy.configure(state='disabled')
            self.btnpaste.configure(state='disabled')

            self.btnrun = ctk.CTkButton(self.topframeb, text='Run', width=130, fg_color='green4', hover_color='green', text_color='white', font=('arial', 14, 'bold'), 
                                        border_width=2, border_color='dark green', command=lambda:runcmdselection(self))
            self.btnrun.pack(pady=7)

        def dataframe(self):
            self.dataframemain = ctk.CTkFrame(master=self, height=259, fg_color="gray64") # (#259,285) Removes white line at bottom of treeview devices
            self.dataframemain.pack(fill='x', side='top') # The background strip runs accross the frame treeview and cmd area
            self.dataframemain.pack_propagate(False)

            self.devtblframea = ctk.CTkFrame(self.dataframemain, fg_color="green4") # Draws a boder arround the Devices treeview
            self.devtblframea.pack(fill='both', expand=True, side='left')
            self.devtblframea.pack_propagate(False)

            # This is where the list of commands are shown
            self.cmdframea = ctk.CTkFrame(self.dataframemain, height=100, fg_color="gray64")
            self.cmdframea.pack(fill='both', expand=True, side='left')
            self.cmdframea.pack_propagate(False)


            # Small strip at bottom of cmd selection box removed from app.
            #self.cmdframeb = ctk.CTkFrame(self.cmdframea, height=60, fg_color="light steel blue")
            #self.cmdframeb.pack(fill='x', expand=False, side='bottom')
            #self.cmdframeb.pack_propagate(False)

            # Label Placeholders
            #self.lbldevtblframea = ctk.CTkLabel(self.devtblframea, text="devtblframea").pack()
            self.lblcmdframea = ctk.CTkLabel(self.cmdframea, text="cmdframea").pack()
            #self.lblcmdframeb = ctk.CTkLabel(self.cmdframeb, text="cmdframeb").pack()

        def dataoutput(self):
            self.dataoutputa = ctk.CTkFrame(master=self, height=565, fg_color="gray64") # OUTPUT DATA AREA
            self.dataoutputa.pack(fill='both', expand=True, side='bottom')
            self.dataoutputa.pack_propagate(False)

            self.dataoutputb = ctk.CTkFrame(self.dataoutputa, height=40, fg_color="gray64")
            self.dataoutputb.pack(fill='x', expand=False, side='bottom')
            self.dataoutputb.pack_propagate(False)
            
            # Label Placeholders
            #self.lbldataoutputa = ctk.CTkLabel(self.dataoutputa, text="dataoutputa").pack()
            #self.lbldataoutputb = ctk.CTkLabel(self.dataoutputb, text="dataoutputb").pack()

            # FRAME 2 (BOTTOM FRAME OF PANNED WINDOW)
            self.txtcontent = scrolledtext.ScrolledText(self.dataoutputa, wrap=WORD, background='light goldenrod yellow') #, height=23, width=83
            self.txtcontent.pack(expand=TRUE, fill='both', padx=2, pady=2) # shows orange arround output
            self.txtcontent.pack_propagate(0)
            
            self.btnsaveoutput = ctk.CTkButton(self.dataoutputb, text='Save output', width=135, fg_color='dark slate gray', command=lambda: saveframeoutput(self), 
                                               font=('arial', 12, 'bold'), hover_color='Gray24', border_width=3, border_color='dark slate gray')
            self.btnsaveoutput.pack(side='left', padx=5)
            self.btnclearoutput = ctk.CTkButton(self.dataoutputb, text='Clear', width=105, fg_color='dark slate gray', command=lambda: self.txtcontent.delete("1.0", END), 
                                                font=('arial', 12, 'bold'), hover_color='Gray24', border_width=3, border_color='dark slate gray') # self.txtcontent.delete("1.0", END) cleartext
            self.btnclearoutput.pack(side='left', padx=5)

            #self.lblstatusoutputa = ctk.CTkLabel(self.dataoutputb, text="statusoutput").pack(side='right')
            self.lbldevicestatus = ctk.CTkLabel(self.dataoutputb, textvariable=self.devicestatus).pack(side='left', padx=(20,5)) # Device ID:
            self.lblstatusoutputgrp = ctk.CTkLabel(self.dataoutputb, textvariable=self.statusvargrp).pack(side='left', padx=(5,5))
            self.lblstatuscmd = ctk.CTkLabel(self.dataoutputb, text=" | Run Method: ").pack(side='left', padx=(5,5))
            self.lblstatusoutputcmd = ctk.CTkLabel(self.dataoutputb, textvariable=self.statusvarcmd).pack(side='left', padx=(5,20))
            self.lblstatuscmd2 = ctk.CTkLabel(self.dataoutputb, text=" | Run Command: ").pack(side='left', padx=(5,20))
            self.lblstatusoutputcmd2 = ctk.CTkLabel(self.dataoutputb, textvariable=self.statusvarcmd2).pack(side='left')

            self.btnexit = ctk.CTkButton(self.dataoutputb, text='Exit', width=85, fg_color='red3', command=self.destroy, font=('arial', 12, 'bold'), hover_color='red4', border_width=2, border_color='red4') # self.txtcontent.delete("1.0", END) cleartext
            self.btnexit.pack(side='right', padx=5)


        topFrame(self)
        dataframe(self)
        #bottomframe(self)
        dataoutput(self)

        try:
            fetchalldb(self, self.devtblframea, self.iam_selecteddevice)
        except:
            print("Unable to load database of devices")
        
        groupswitch_event(self)


if __name__ == "__main__":
    app = App()
    app.mainloop()

    



main.py
jolders/devnetnode
# Location of files
# dbfile = "C:\\Shared\devnetnode\\devnetnode\\netAdmin.db"
# directorypathroot ="C:\\Shared\devnetnode\\devnetnode"
# devicegroups = "C:\\Shared\devnetnode\\devnetnode\\devicegroups"
# pythonicon = "C:\\Shared\devnetnode\\devnetnode\\myicon64.png"
# devicesaveddata = "C:\\Shared\devnetnode\\devnetnode\\devicedata\\"
# tftprootdir = "C:\\Shared\devnetnode\\devnetnode\\devicedata\\tftp_temp"

#TFTP_SERVER = tftpserver (Windows)
# directorylocation = "C:\\Shared\devnetnode\\devnetnode\\devicedata\\tftp_temp\\"
# filesource = "C:\\Shared\devnetnode\\devnetnode\\tftp_temp\\"
#filedestination = "C:\\Shared\devnetnode\\devnetnode\\devicedata\\{hostname}\\"
# changetoDEVICEDATAdirectory = "C:\\Shared\devnetnode\\devnetnode"

tftpserver = '10.24.35.253'
# tftpserver = '192.168.0.123'
# sudo apt install tftpd-hpa
# $ cat /etc/default/tftpd-hpa
# sudo nano /etc/default/tftpd-hpa
#/etc/default/tftpd-hpa 
# TFTP_USERNAME="tftp" 
# TFTP_DIRECTORY="/srv/tftp" 
# TFTP_ADDRESS=":69" 
# TFTP_OPTIONS="--secure --create"
# sudo chown tftp tftp_temp/
# service tftpd-hpa restart  
# sudo systemctl status tftp-hpa.service
# sudo systemctl restart tftp-hpa.service

sideframecolour = 'light blue'
bottomframecolour = 'light blue'
topframeacolour = 'light blue'
topframebcolour = 'light blue'

# Linux Format of file locations
dbfile = "/Shared/devnetnode/devnetnode/netAdmin.db"

directorypathroot ="/Shared/devnetnode/devnetnode/"
devicegroups = "/Shared/devnetnode/devnetnode/devicegroups"
pythonicon = "/Shared/devnetnode/devnetnode/netdevnode2.png"
#pythonicon = "/Shared/devnetnode/devnetnode/pythonicon.ico"
devicesaveddata = "/Shared/devnetnode/devnetnode/devicedata/"
tftprootdir = "/Shared/devnetnode/devnetnode/devicedata/tftp_temp"

#TFTP_SERVER = tftpserver Linux
directorylocation = "/Shared/devnetnode/devnetnode/devicedata/tftp_temp/"
filesource = '/Shared/devnetnode/devnetnode/devicedata/tftp_temp/'
filedestination = '/Shared/devnetnode/devnetnode/devicedata/'
changetoDEVICEDATAdirectory = '/Shared/devnetnode/devnetnode'

listOfCMDs = ['show version',
              'show running-config',
              'show ip interface brief',
              'show ip route',
              'show interface gigabitEthernet 0/0',
              'show interface gigabitEthernet 0/1',
              'show interface gigabitEthernet 0/2',
              'show interface gigabitEthernet 0/3',
              'show interface gigabitEthernet 0/4',
              'show interface gigabitEthernet 1/0',
              'show interface gigabitEthernet 1/1',
              'show arp',
              'show flash:',
              'show vlan']

print("(settings.py) = page test")

# Permission change to folder for TFTP
# chown -R tftp /Shared/NetDevApp3/cisco10/devicedata/tftp_temp



settings.py
jolders/devnetnode
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
from tkinter import scrolledtext
from settings import *
import sqlite3
from mysql.connector import Error
from paramiko.client import SSHClient, AutoAddPolicy
from paramiko import client, ssh_exception
from netmiko import Netmiko, ConnectHandler, NetMikoTimeoutException, NetMikoAuthenticationException
import time

def getdevicefromid(rowIDrow): 
    print("I am 'getdevicefromid' row id: " + str(rowIDrow))
    try:
        sqliteConnection = sqlite3.connect(dbfile)
        cursor = sqliteConnection.cursor()
        #record = cursor.fetchall()
        query = (f"SELECT * FROM devices WHERE id = {rowIDrow}")
        cursor.execute(query)
        record = cursor.fetchall()
        for row in record:
            retid = row[0]
            retname = row[1]
            retip = row[2]
            retusername = row[3]
            retpassword = row[4]
            
        return retid, retname, retip, retusername, retpassword
        
    except sqlite3.Error as error:
        print("The error (connecttodb.py) message is: ", error)
        print("Exception class is: ", error.__class__)
        print("Exception is", error.args)

def runcmdparamiko(self, iam_selecteddevice):
    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retid, retname, retip, retusername, retpassword = getdevicefromid(iam_selecteddevice)
    
    print(f"This is runcmdparamiko (self.cmdvarselected.get()): {self.cmdvarselected.get()}")
    print(f"This is runcmdparamiko (iam_selecteddevice): {iam_selecteddevice}")
    print(f"This is runcmdparamiko (id): {str(retid)}")
    print(f"This is runcmdparamiko (name): {retname}")
    print(f"This is runcmdparamiko (ip): {retip}")
    print(f"This is runcmdparamiko (username): {retusername}")
    print(f"This is runcmdparamiko (password): {retpassword}")
    
    ssh_client = client.SSHClient()
    #client.load_host_keys()
    ssh_client.set_missing_host_key_policy(client.AutoAddPolicy())

    ###################
    # Starting the process so feedback on status label
    #####################
    self.returnedname.set(retname) # FEEDBACK Name to main.py
    print(f"runcmdparamiko value setting self.returnedname: {self.returnedname.get()}")
    
    try:
        ssh_client.connect(hostname=retip, port=22, username=retusername, password=retpassword) # look_for_keys=False, allow_agent=False
        print("SSH CONNECTION successfull")
        cmd = self.cmdvarselected.get()
        print(cmd)
        stdin, stdout, stderr = ssh_client.exec_command(cmd)
        self.txtcontent.insert(INSERT, f"- - - - * - - - - * - - - - * - START (" + retname + ") - * - - - - * - - - - * - - - - " + "\n")
        for line in stdout.readlines():
            testvar=line.strip()
            self.txtcontent.insert(INSERT, f"{testvar}" + "\n")
        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - - * - - - - * - - END (" + retname + ") - - * - - - - * - - - - * - - - - " + "\n")
    
    except ssh_exception.NoValidConnectionsError as sshNoValidException:
        print("(sshservices.py 77): No Valid Connections Error SSH connection 'USERNAME ?': %s" % sshNoValidException)
    
    except ssh_exception.AuthenticationException as authException:
        print("(sshservices.py 79): Authentication Type SSH connection exception 'PASSWORD ?': %s" % authException)
    except ssh_exception.BadAuthenticationType as authTypeException:
        print("(sshservices.py 81): Authentication SSH connection exception: %s" % authTypeException)
    except ssh_exception.SSHException as sshException:
        print("(sshservices.py 83): Unable to establish SSH connection: %s" % sshException)
    except Exception as unknown_error:
        print('(sshservices.py 85):Some other error: ' + str(unknown_error))
        self.txtcontent.insert(INSERT, f"- - - - * - - - - * - - - - * - START (" + retname + ") - * - - - - * - - - - * - - - - " + "\n")
        self.txtcontent.insert(INSERT, "\n" + f"{unknown_error}" + "\n")
        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - - * - - - - * - - END (" + retname + ") - - * - - - - * - - - - * - - - - " + "\n")
    
    finally:
        ssh_client.close()

        
def runcmdnetmiko(self, iam_selecteddevice):
    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retid, retname, retip, retusername, retpassword = getdevicefromid(iam_selecteddevice)
    print(f"This is runcmdnetmiko (iam_selecteddevice): {iam_selecteddevice}")
    print(f"This is runcmdparamiko (self.cmdvarselected.get()): {self.cmdvarselected.get()}")
    print(f"This is runcmdparamiko (id): {str(retid)}")
    print(f"This is runcmdparamiko (name): {retname}")
    print(f"This is runcmdparamiko (ip): {retip}")
    print(f"This is runcmdparamiko (username): {retusername}")
    print(f"This is runcmdparamiko (password): {retpassword}")
    
    credentials = {
        'device_type': 'cisco_ios',
        'host': f'{retip}',
        'username': f'{retusername}',
        'password': f'{retpassword}'}
    
    time.sleep(1)
    self.returnedname.set(retname) # FEEDBACK Name to main.py
    print(f"runcmdnetmiko value setting self.returnedname: {self.returnedname.get()}")
    
    try:
        print(f"This is runcmdnetmiko - starting try")
        net_connect = Netmiko(**credentials)
        dev_prompt = net_connect.find_prompt()
        print(f"Device in Enable mode? : {net_connect.check_enable_mode()}")
        print(f"Device in Config mode? : {net_connect.check_config_mode()}")
        sh_output = net_connect.send_command(self.cmdvarselected.get())
        net_connect.disconnect()
        # self.statuslbl.set(f"Successfull: ({dev_prompt})")
        print(f"This is runcmdnetmiko - ending try")
    except (NetMikoTimeoutException, NetMikoAuthenticationException) as e:
        print(f"This is runcmdnetmiko (error,exception)= {e}")
        # self.statuslbl.set(f"Connection Error to : ({retip})")
    finally:
        try:
            if net_connect:
                net_connect.disconnect()
        except:
            print("Exception (sshservices.py Netmiko) in net_connect")
        

        self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
        try:
            self.txtcontent.insert(INSERT, f"{sh_output}")
        except:
            self.txtcontent.insert(INSERT, f"Error connecting to device")
        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + retname + ")   * - - - * - - - - * - - - - " + "\n" + "\n")

    self.returnedname.set(retname)

def runcmdnetmiko_ch(self, iam_selecteddevice):
    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retid, retname, retip, retusername, retpassword = getdevicefromid(iam_selecteddevice)
    print(f"This is runcmdnetmiko_ch (iam_selecteddevice): {iam_selecteddevice}")
    print(f"This is runcmdnetmiko_ch (self.cmdvarselected.get()): {self.cmdvarselected.get()}")
    print(f"This is runcmdnetmiko_ch (id): {str(retid)}")
    print(f"This is runcmdnetmiko_ch (name): {retname}")
    print(f"This is runcmdnetmiko_ch (ip): {retip}")
    print(f"This is runcmdnetmiko_ch (username): {retusername}")
    print(f"This is runcmdnetmiko_ch (password): {retpassword}")
    
    cisco_device = {
        'device_type': 'cisco_ios',
        'host': retip,
        'username': retusername,
        'password': retpassword,
        'port': 22,
        'secret': retpassword,
        'verbose': True}
    
    output=""
    self.txtcontent.insert(INSERT, f"- - - - * - - - - * - START - * - - - - * - - - - " + "\n")

    self.returnedname.set(retname) # FEEDBACK Name to main.py
    print(f"runcmdnetmiko_ch value setting self.returnedname: {self.returnedname.get()}")

    try:
        conn = ConnectHandler(**cisco_device)
        conn_prompt = conn.find_prompt()
        if '>' in conn_prompt: conn.enable()
        # print(conn_prompt)
        output = conn.send_command(self.cmdvarselected.get())
        
        print(f"Checking the config mode: {conn.check_config_mode()}")
        
    except:
        print("A connection error in runcmdnetmiko_ch")
    finally:
        # An un-captured error occurred here if unable to log in to router
        conn.disconnect()
    
    self.txtcontent.insert(INSERT, f"{output}")
    self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - - * -  END  - * - - - - * - - - - " + "\n")
    
    '''
    output=""
    try:
        print(f"This is runcmdnetmiko")
        self.txtcontent.insert(INSERT, "Conecting...........")
        connection = Netmiko(host=ip, port=22, username=username, password=password, device_type='cisco_ios')
        output=connection.send_command(cmdselected)
        connection.disconnect()
    except (NetMikoTimeoutException, NetMikoAuthenticationException) as e:
        print(f"This is runcmdnetmiko (error)= {e}")
    finally:
        if connection == True: 
            connection.disconnect()
            print(f"Connection disconnect runcmdnetmiko")
        self.txtcontent.insert(INSERT, f"{output}")
    '''
    
    
# # # # # # # # # # ----- RAW Paramiko SSH  ----- # # # # # # # # # # 
''' # SAVE AS AN EXAMPLE OF ALTERNATIVE SSH CONNECTION VIA SHELL
def pariamiko_raw(self, ip, username, password):

    client = SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    deviceconnection = {'hostname': '', 'port': '22', 'username': '', 'password': ''}
    deviceconnection["hostname"]=ip
    deviceconnection["username"]=username
    deviceconnection["password"]=password

    
    try:
        print(f"Trying to connect to {deviceconnection['hostname']}")
        client.connect(**deviceconnection, look_for_keys=False, allow_agent=False)
        print(client.get_transport().is_active())
        
        shell = client.invoke_shell()
        shell.send('terminal length 0\n')
        time.sleep(.2)
        shell.send('show run\n')
        time.sleep(5)
        output = shell.recv(65535)
        time.sleep(.2)
        mystr = output.decode(encoding='utf-8')
        print(mystr)
        self.txtcontent.insert(INSERT, f"{mystr}")
    except:
        print("FAILED TO CONNECT")
        print(client.get_transport().is_active())
    finally:
        client.close()
        print("CLOSING CONNECTION TO THE DEVICE")
'''


def runcmdnetmiko_ch_config(self, iam_selecteddevice):
    retid=0
    retname=""
    retip=""
    retusername=""
    retpassword=""
    retid, retname, retip, retusername, retpassword = getdevicefromid(iam_selecteddevice)
    print(f"This is runcmdparamiko (iam_selecteddevice): {iam_selecteddevice}")
    print(f"This is runcmdparamiko (id): {str(retid)}")
    print(f"This is runcmdparamiko (name): {retname}")
    print(f"This is runcmdparamiko (ip): {retip}")
    print(f"This is runcmdparamiko (username): {retusername}")
    print(f"This is runcmdparamiko (password): {retpassword}")

    
    cmd_list =[]
    thisoutput=StringVar()
    conn_prompt=""
    
    credentials = {
        'device_type': 'cisco_ios',
        'host': retip,
        'username': retusername,
        'password': retpassword,
        'port': 22,
        'secret': retusername,
        'verbose': True}
    
    # print(f"TASK 1 runcmdnetmiko_ch_config (cmds): ({iam_selecteddevice})")
    self.txtcontent.insert(INSERT, f"- - - - * - - - - * - START - * - - - - * - - - - " + "\n")
    
    # get the contents of the listbox and give it a list
    custcmdsget = self.cmdtxtbox.get("1.0", END)
    print(f"I am  in [cmdframebox.printcustcmds]: {custcmdsget}")
    for line in custcmdsget.split("\n"):
        print(f"{line}")
        #self.varselected.set(custcmdsget)
    cmd_list = [y for y in (x.strip() for x in custcmdsget.splitlines()) if y]
    print(f"I am  in [cmdlist.printcustcmds]{cmd_list}")
    # self.varselected.set(cmd_list)

    self.returnedname.set(retname) # FEEDBACK Name to main.py
    print(f"runcmdnetmiko_ch_config value setting self.returnedname: {self.returnedname.get()}")

    try:
        print(f"Try block 1 (cmds): ({cmd_list})")
        net_connect = ConnectHandler(**credentials)
        # Enter the ENABLE command on the router
        conn_prompt = net_connect.find_prompt()
        if '>' in conn_prompt: net_connect.enable()
        # print(f"Try block 2 (con prompt): ({conn_prompt})")
        thisoutput= net_connect.send_config_set(cmd_list)
        self.txtcontent.insert(INSERT, f"{thisoutput}")
        
    except (NetMikoTimeoutException, NetMikoAuthenticationException) as e:
        print(f"Netmiko_CH_config error: {e}")
        
    finally:
        #net_connect.exit_config_mode()
        #print(f"Finally 1 whats the command prompt: ({conn_prompt})")
        wmout=StringVar()
        wmout = net_connect.send_command('write memory')
        net_connect.disconnect()
        
        self.txtcontent.insert(INSERT, f"{wmout}")
        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - - * -  END  - * - - - - * - - - - " + "\n")

sshservices.py
jolders/devnetnode
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
from tkinter import scrolledtext
import sqlite3
from mysql.connector import Error
import paramiko
from paramiko.client import SSHClient, AutoAddPolicy
from paramiko import client
from netmiko import Netmiko, ConnectHandler, NetMikoTimeoutException, NetMikoAuthenticationException
import time
import os
from settings import *
import json
from connecttodb import getdevicefromid

def getdevicefromidgrp(rowIDrow): 
    print("I am 'getdevicefromid' row id: " + str(rowIDrow))
    try:
        sqliteConnection = sqlite3.connect(dbfile)
        cursor = sqliteConnection.cursor()
        #record = cursor.fetchall()
        query = (f"SELECT * FROM devices WHERE id = {rowIDrow}")
        cursor.execute(query)
        record = cursor.fetchall()
        for row in record:
            retid = row[0]
            retname = row[1]
            retip = row[2]
            retusername = row[3]
            retpassword = row[4]
            
        return retid, retname, retip, retusername, retpassword
        
    except sqlite3.Error as error:
        print("The error (connecttodb.py) message is: ", error)
        print("Exception class is: ", error.__class__)
        print("Exception is", error.args)

def grpselectionget(self, groupselection):
    # Group selection get values
    print("Groupselection (sshservicesgrp/grpselectionget()) get values")
    # What directory am I in
    print(f"1) The current working directory is (SSHSERVICESGRP.py): {os.getcwd()}")

    # Change the directory to 'London routers' = 'devicegroups'
    os.chdir(devicegroups)
    groupdevicepath = os.path.join(devicegroups, groupselection)
    os.chdir(groupdevicepath)
    print(f"1a) The current working directory is (SSHSERVICESGRP.py): {os.getcwd()}")
    print(f"1b) The current working directory is (SSHSERVICESGRP.py): {os.getcwd()}")
    # Get the json file
    filepath = os.path.join(groupselection + ".json")
    print(f"1b) (SSHSERVICESGRP.py) filepath = {filepath}")
    filetoread = open(filepath)
    inputjsondata = json.load(filetoread)
    print(f"1c) Data in members: {inputjsondata['members']}")
    return inputjsondata['members']
    # Extract the data from the json file

def runcmdparamikogrp(self, groupselection, cmdselected):
    print(f"1a) This is runcmdparamikogrp (groupselection): {groupselection}")
    print(f"2a) This is runcmdparamikogrp (cmdselected): {cmdselected}")
    grpmemberreturn = grpselectionget(self, groupselection)
    print(f"3) These are the grp members this is runcmdparamikogrp (grpmemberreturn): {grpmemberreturn}")
    print(f"4) This is runcmdparamikogrp (groupselection): {groupselection}")
    print(f"5) This is runcmdparamikogrp (cmdselected): {cmdselected}")
    
    for i in grpmemberreturn:
        retid=0
        retname=""
        retip=""
        retusername=""
        retpassword=""
        #retdescription=[]

        
        print(i)
        getdevicefromidgrp(i)
        
        try:
            retid, retname, retip, retusername, retpassword = getdevicefromidgrp(i)
        except TypeError:
                print(f"Printing load device 'Try block is error exception' ")
        
        print(f"Printing load device 'Try block is success' ")
        print(f"retid: {retid}")
        print(f"retname: {retname}")
        print(f"retip: {retip}")
        print(f"retusername: {retusername}")
        print(f"retpassword: {retpassword}")
        #print(f"retdescription: {retdescription}")

        ssh_client = client.SSHClient()
        #client.load_host_keys()
        ssh_client.set_missing_host_key_policy(client.AutoAddPolicy())

        try:
            ssh_client.connect(hostname=retip, port=22, username=retusername, password=retpassword) # look_for_keys=False, allow_agent=False
            print("SSH CONNECTION successfull")
            cmd = cmdselected
            stdin, stdout, stderr = ssh_client.exec_command(cmd)
            # print(stdout.readlines())
            self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
            for line in stdout.readlines():
                testvar=line.strip()
                self.txtcontent.insert(INSERT, f"{testvar}" + "\n")
            self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
        except:
            self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   FAILED TO CONNECT  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
        finally:
            ssh_client.close()
        
def runcmdnetmikogrp(self, groupselection, cmdselected):
    grpmemberreturn = grpselectionget(self, groupselection)
    self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + groupselection + ")   * - - - * - - - - * - - - - " + "\n")
    for i in grpmemberreturn:
        retid=0
        retname=""
        retip=""
        retusername=""
        retpassword=""
        #retdescription=""
        
        print(i)
        # getdevicefromid(i)

        try:
            retid, retname, retip, retusername, retpassword = getdevicefromidgrp(i)
        except TypeError:
                print(f"Printing load device 'Try block is error exception' ")
        
        print(f"Printing load device 'Try block is success' ")
        print(f"retid: {retid}")
        print(f"retname: {retname}")
        print(f"retip: {retip}")
        print(f"retusername: {retusername}")
        print(f"retpassword: {retpassword}")
        #print(f"retdescription: {retdescription}")
        
        credentials = {'device_type': 'cisco_ios', 'host': f'{retip}', 'username': f'{retusername}', 'password': f'{retpassword}'}
        
        print(f"This is runcmdnetmikogrp (ip): {retip}")
        print(f"This is runcmdnetmikogrp (username): {retusername}")
        print(f"This is runcmdnetmikogrp (password): {retpassword}")
        print(f"This is runcmdnetmikogrp (cmdselected): {cmdselected}")
        
        time.sleep(0.3)
        
        try:
            print(f"This is runcmdnetmikogrp - starting try")
            net_connect = Netmiko(**credentials)
            dev_prompt = net_connect.find_prompt()
            print(f"Device in Enable mode? : {net_connect.check_enable_mode()}")
            print(f"Device in Config mode? : {net_connect.check_config_mode()}")
            sh_output = net_connect.send_command(cmdselected)
            net_connect.disconnect()
            self.statuslbl.set(f"Successfull: ({dev_prompt})")
            print(f"This is runcmdnetmikogrp - ending try")
        except (NetMikoTimeoutException, NetMikoAuthenticationException) as e:
            print(f"This is runcmdnetmikogrp (error,exception)= {e}")
            self.statuslbl.set(f"Connection Error to : ({retip})")
        finally:
            if net_connect == True:
                net_connect.disconnect()

            # DISPLAY THE LOOP OUTPUT TO THE USER
            self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
            self.txtcontent.insert(INSERT, f"{sh_output}")
            self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + retname + ")   * - - - * - - - - * - - - - " + "\n" + "\n")

    self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + groupselection + ")   * - - - * - - - - * - - - - " + "\n")

def runcmdnetmikogrp_ch(self, groupselection, cmdselected):
    print(f"1) This is runcmdnetmikogrp_ch (groupselection): {groupselection}")
    print(f"2) This is runcmdnetmikogrp_ch (cmdselected): {cmdselected}")
    grpmemberreturn = grpselectionget(self, groupselection)
    self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + groupselection + ")   * - - - * - - - - * - - - - " + "\n")
    for i in grpmemberreturn:
        retid=0
        retname=""
        retip=""
        retusername=""
        retpassword=""
        #retdescription=""
        
        print(i)
        # getdevicefromid(i)

        try:
            retid, retname, retip, retusername, retpassword = getdevicefromidgrp(i)
        except TypeError:
                print(f"3) Printing load device 'Try block is error exception' ")
        
        print(f"4) Printing load device 'Try block is success' ")
        print(f"5) retid: {retid}")
        print(f"6) retname: {retname}")
        print(f"7) retip: {retip}")
        print(f"8) retusername: {retusername}")
        print(f"9) retpassword: {retpassword}")
        #print(f"10) retdescription: {retdescription}")

        credentials = {'device_type': 'cisco_ios', 'host': f'{retip}', 'username': f'{retusername}', 'password': f'{retpassword}'}
        
        print(f"11) This is runcmdnetmikogrp_ch (ip): {retip}")
        print(f"12) This is runcmdnetmikogrp_ch (username): {retusername}")
        print(f"13) This is runcmdnetmikogrp_ch (password): {retpassword}")
        print(f"14) This is runcmdnetmikogrp_ch (cmdselected): {cmdselected}")

        cisco_device = {'device_type': 'cisco_ios', 'host': retip, 'username': retusername,
                         'password': retpassword, 'port': 22, 'secret': retpassword, 'verbose': True}
        
        try:
            conn = ConnectHandler(**cisco_device)
            conn_prompt = conn.find_prompt()
            if '>' in conn_prompt: conn.enable()
            # print(conn_prompt)
            output = conn.send_command(cmdselected)
            print(f"Checking the config mode: {conn.check_config_mode()}")

        except:
            print("A connection error in runcmdnetmiko_ch")
        
        finally:
            conn.disconnect()
            self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + retname + ")   * - - - * - - - - * - - - - " + "\n")
            self.txtcontent.insert(INSERT, f"{output}")
            self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + retname + ")   * - - - * - - - - * - - - - " + "\n" + "\n")

        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - * - - - - *   END  (" + groupselection + ")   * - - - * - - - - * - - - - " + "\n")

def runcmdnetmikogrp_ch_config(self, groupselection): #, cmdselected
    print(f"1) This is runcmdnetmikogrp_ch_config (groupselection): {groupselection}")
    #print(f"2) This is runcmdnetmikogrp_ch_config (cmdselected): {cmdselected}")
    grpmemberreturn = grpselectionget(self, groupselection)
    self.txtcontent.insert(INSERT, "- - - - * - - - * - - - - *   START  (" + groupselection + ")   * - - - * - - - - * - - - - " + "\n")

    for i in grpmemberreturn:
        retid=0
        retname=""
        retip=""
        retusername=""
        retpassword=""
        #retdescription=""

        print(i)
        # getdevicefromid(i)

        try:
            retid, retname, retip, retusername, retpassword = getdevicefromidgrp(i)
        except TypeError:
                print(f"3) Printing load device 'Try block is error exception' ")
        
        print(f"4) Printing load device 'Try block is success' ")
        print(f"5) retid: {retid}")
        print(f"6) retname: {retname}")
        print(f"7) retip: {retip}")
        print(f"8) retusername: {retusername}")
        print(f"9) retpassword: {retpassword}")
        #print(f"10) retdescription: {retdescription}")

        credentials = {'device_type': 'cisco_ios', 'host': f'{retip}', 'username': f'{retusername}', 'password': f'{retpassword}'}
        
        print(f"11) This is runcmdnetmikogrp_ch (ip): {retip}")
        print(f"12) This is runcmdnetmikogrp_ch (username): {retusername}")
        print(f"13) This is runcmdnetmikogrp_ch (password): {retpassword}")
        #print(f"14) This is runcmdnetmikogrp_ch (cmdselected): {cmdselected}")

        cisco_device = {'device_type': 'cisco_ios', 'host': retip, 'username': retusername,
                         'password': retpassword, 'port': 22, 'secret': retpassword, 'verbose': True}
        
        cmd_list =[]
        thisoutput = StringVar()
        conn_prompt = ""
        
        #print(f"TASK 1 runcmdnetmikogrp_ch_config (cmds): ({cmdselected})")
        
        # get the contents of the listbox and give it a list
        # print("printcustcmds")
        custcmdsget = self.cmdtxtbox.get("1.0", END)
        # print(f"I am  in [cmdframebox.printcustcmds]: {custcmdsget}")
        for line in custcmdsget.split("\n"):
             print(f"runcmdnetmikogrp_ch_config (cmds): {line}")
        #self.varselected.set(custcmdsget)
        cmd_list = [y for y in (x.strip() for x in custcmdsget.splitlines()) if y]
        print(f"I am  in [cmdlist.printcustcmds]{cmd_list}")
        # self.varselected.set(cmd_list)
        
        try:
            print(f"Try block 1 (cmds): ({cmd_list})")
            net_connect = ConnectHandler(**credentials)
            # Enter the ENABLE command on the router
            conn_prompt = net_connect.find_prompt()
            if '>' in conn_prompt: net_connect.enable()
            # print(f"Try block 2 (con prompt): ({conn_prompt})")
            thisoutput= net_connect.send_config_set(cmd_list)
            self.txtcontent.insert(INSERT, f"{thisoutput}")
            
        except (NetMikoTimeoutException, NetMikoAuthenticationException) as e:
             print(f"Netmiko_CH_config error: {e}")
             self.txtcontent.insert(INSERT, f"{e}")
        
        finally:
            # net_connect.exit_config_mode()
            # print(f"Finally 1 whats the command prompt: ({conn_prompt})")
            wmout=StringVar()
            wmout = net_connect.send_command('write memory')
            net_connect.disconnect()
            self.txtcontent.insert(INSERT, f"{wmout}")
        
        self.txtcontent.insert(INSERT, "\n" + "- - - - * - - - - * -  END  - * - - - - * - - - - " + "\n")
sshservicesgrp.py
jolders/devnetnode
import sqlite3
from mysql.connector import Error
import os
from settings import *
from tkinter import *
import tkinter as tk
from tkinter import ttk
import customtkinter as ctk
from tkinter import ttk, scrolledtext, messagebox
import shutil
from netmiko import Netmiko, ConnectHandler, NetMikoTimeoutException, NetMikoAuthenticationException
from connecttodb import getdevicefromid
from PIL import ImageTk, Image
import regex as re
from netmiko.exceptions import NetMikoAuthenticationException
from netmiko.exceptions import NetMikoTimeoutException
from netmiko.exceptions import SSHException
# from netmiko.ssh_exception import NetMikoAuthenticationException
# from netmiko.ssh_exception import NetMikoTimeoutException
# from netmiko.ssh_exception import SSHException

# this file uploads a configuration file to a Cisco device
# configuploadtftp
# Pulled in from the right click on treeview on devices may not be required
def configuploadtftp(self):
    devicesdetails = []
    self.tftpselectedid=StringVar()
    self.setbackuptypevar=StringVar() # Bakup type (startup, running)
    self.setfileidtypevar=StringVar() # File identifier (weekly, golden ?)
    self.filename = StringVar()
    self.filedest = StringVar()
    self.filesrc = StringVar()
    self.tftprootdir = StringVar()
    self.tftprootdir.set(tftprootdir) # C:\\Shared\\NetDevApp3\\cisco12ct\\devicedata\\tftp_temp

    self.checkboxcopy_var = ctk.StringVar(value="on") # Checkbox value for COPY/MERGE selection
    self.checkboxrepl_var = ctk.StringVar(value="off") # Checkbox value for REPLACE selection
    self.checkboxcrs_var = ctk.StringVar(value="off") # Checkbox value for COPYRUNSTART selection
    self.chkbxrunningcfg_var = ctk.StringVar(value="on") # running config
    self.chkbxstartupcfg_var = ctk.StringVar(value="off") # startup config
    self.checkboxcstrn_var = ctk.StringVar(value="off") # 'copy running > startup'

    self.tftpuploadcmd = ctk.StringVar()

    # Evaluate the value of the checkbox to determin running or startup
    self.running_startup_var = StringVar()

    self.returnedid = IntVar(self)
    self.returnedname = StringVar()
    self.returnedip = StringVar()
    self.returnedusername = StringVar()
    self.returnedpassword = StringVar()
    self.returneddescription = StringVar()
    self.returnedtype = StringVar()
    self.returnedgolden = StringVar()

    sqliteConnection = sqlite3.connect(dbfile)
    cursor = sqliteConnection.cursor()
    #record = cursor.fetchall()
    query = (f"SELECT * FROM devices")
    cursor.execute(query)
    record = cursor.fetchall()
    for row in record:
        self.returnedid.set(row[0])
        self.returnedname.set(row[1])
        self.returnedip.set(row[2])
        self.returnedusername.set(row[3])
        self.returnedpassword.set(row[4])
        self.returneddescription.set(row[5])
        self.returnedtype.set(row[6])
        self.returnedgolden.set(row[7])
        #retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getalldevices(self)
        devicesdetails.append(f"id:{str(self.returnedid.get())}" + " " +  f"{self.returnedname.get()}" + " " + f"({self.returnedip.get()})")
        # print(f"{retid},{retname},{retip},{retusername},{retpassword},{retdescription},{rettype},{retgolden}")
        self.tftpselectedid.set(str(self.returnedid.get()))
    
    print("1) This is configuploadtftp(uploadtftp.py)")
    # print(f"2) This is configuploadtftp (iam_selecteddevice): {str(iam_selecteddevice)}")
    print(f"3) This is configuploadtftp (id): {str(self.returnedid.get())}")
    print(f"4) This is configuploadtftp (name): {self.returnedname.get()}")
    print(f"5) This is configuploadtftp (ip): {self.returnedip.get()}")
    print(f"6) This is configuploadtftp (username): {self.returnedusername.get()}")
    print(f"7) This is configuploadtftp (password): {self.returnedpassword.get()}")
    print(f"8) This is configuploadtftp (retdescription): {self.returneddescription.get()}")
    print(f"9) This is configuploadtftp (rettype): {self.returnedtype.get()}")
    print(f"10) This is configuploadtftp (retgolden): {self.returnedgolden.get()}")
    

    # Pressing the 'Upload button'
    def tftptorouter(self):
        self.progstatus.set(0.3)
        self.btnupload.configure(state='disabled', fg_color='gray', border_color='gray') # Button
        self.checkboxcopy.configure(state='disabled', fg_color='gray', border_color='gray') # "Copy(merge) config"
        self.checkboxrepl.configure(state='disabled', fg_color='gray', border_color='gray') # "Replace running config"
        self.checkboxcrs.configure(state='disabled', fg_color='gray', border_color='gray') # "copy runnning > startup"
        self.chkbxrunningcfg.configure(state='disabled', fg_color='gray', border_color='gray') # "running-config"
        self.chkbxstartupcfg.configure(state='disabled', fg_color='gray', border_color='gray') # "startup-config"
        self.checkboxcstrn.configure(state='disabled', fg_color='gray', border_color='gray') # "copy startup > runnning"
        self.update_idletasks()


        #print(f"4a) The current working directory is : {os.getcwd()}")
        #filelocated = '/Shared/NetDevApp3/cisco11/devicedata/tftp_temp/'
        os.chdir(self.tftprootdir.get())
        print(f"4b) Upload to Device name: {self.returnedname.get()}")
        print(f"4c) The current working directory is : {os.getcwd()}")
        # What is the file name
        print(f"4d) File name to look for is: {self.filename.get()}")

        # AT THIS POINT MAKE SURE YOU HAVE GOT ALL THE CISCO SEVICE TYPE LOGIN/ACCESS variable in place
        self.grpdesetry.insert(INSERT, f"2) Looking for file in the TFTP staging area:\n{self.filename.get()}\n")

        # Evaluate the value of the checkbox to determin running or startup
        # self.running_startup_var = StringVar()
        
        if self.chkbxstartupcfg_var.get() == "on":
            self.running_startup_var.set("startup-config")
            print(f"checbxrunningcfg_var value = {self.chkbxrunningcfg_var.get()}")
        elif self.chkbxrunningcfg_var.get() == "on":
            self.running_startup_var.set("running-config")
            print(f"checbxstartupcfg_var value = {self.chkbxstartupcfg_var.get()}")
        else:
            print("uploadtftp.py(tftprouter) error occured getting checbxstartupcfg_var or checbxrunningcfg_var")


        
        if os.path.isfile(self.filename.get()):
            print("4)d File exist so lets go")
            
            self.statuslbl.configure(font=('arial', 12, 'bold'), text='Starting to run task')
            self.update_idletasks()
            
            cisco_device = {
                'device_type': f'{self.returnedtype.get()}',
                'host': f'{self.returnedip.get()}',
                'username': f'{self.returnedusername.get()}',
                'password': f'{self.returnedpassword.get()}',
                'port': 22,
                'secret': f'{self.returnedpassword.get()}',
                'verbose': True}
            
            self.grpdesetry.insert(INSERT, f"3) Initiating Connection to : {self.returnedname.get()}\n")

            conn = ConnectHandler(**cisco_device)
            conn_prompt = conn.find_prompt()
            if '>' in conn_prompt: conn.enable()
            output = conn.send_command(f"\n") # what do enter into the devices command line to accept a tftp upload ?
            print(f"Checking the config mode: {conn.check_config_mode()}")
            self.grpdesetry.insert(INSERT, f"4) The config mode on the device is: {conn.check_config_mode()}\n")
            
            self.progstatus.set(0.4)
            self.update_idletasks()
            
            # Router#copy tftp: running-config
            # Address or name of remote host []? 10.104.207.171
            # Source filename []? backup_cfg_for_my_router
            # Destination filename [running-config]?
            try:
                name = f"device{str(1)}"
                net_connect = ConnectHandler(**cisco_device)
                print("Try 1a")
                net_connect.enable()
                hostname = net_connect.find_prompt()[:-1]
                print(f"The hosname found on the device is: {hostname}")
                self.grpdesetry.insert(INSERT, f"5) The hostname on the device is: {hostname}\n")

                print(f"The value of checkboxcopy_var is: {self.checkboxcopy_var.get()}")

                self.statuslbl.configure(font=('arial', 12, 'bold'), text='Task in progress')
                self.progstatus.set(0.5)
                self.update_idletasks()


                if self.checkboxcopy_var.get() == "on": # Copy(merge) config
                    # Option1 Copy tftp > 'running-config' Option2 Copy tftp > 'startup-config'
                    # option changes due to value in running_startup_var
                    print("Option2 'startup-config checkboxcopy_var")
                    self.tftpuploadcmd.set(f"copy tftp://{tftpserver}{tftprootdir}/{self.filename.get()} {self.running_startup_var.get()}") 
                    self.grpdesetry.insert(INSERT, f"6) Sending the command: {self.tftpuploadcmd.get()}\n")
                    print(f"Try 1b (uploadtftp)copy_command:{self.tftpuploadcmd.get()}")
                    output = net_connect.send_command_timing(self.tftpuploadcmd.get())
                    if "Address or name" in output:
                        output += net_connect.send_command_timing(f"{tftpserver}\n")
                        print("Try 2")
                    if "Source filename" in output:
                        output += net_connect.send_command_timing(f"{self.filename.get()}\n")
                        print("Try 3")
                        net_connect.disconnect
                    if "Destination filename" in output:
                        output += net_connect.send_command_timing(f"\n")
                        print("Try 4 ---- I THINK IT DID IT")
                        self.grpdesetry.insert(INSERT, f"7) Running the command on the device.\n")
                    
                    self.progstatus.set(0.7)
                    self.update_idletasks()

                    # 'running-config' checkbox on + 'copy running > startup' checkbox on
                    print(f"COPY IF~chkbxrunningcfg_var on: {self.chkbxrunningcfg_var.get()}")
                    print(f"COPY IF~checkboxcrs_var on: {self.checkboxcrs_var.get()}")
                    if self.chkbxrunningcfg_var.get() == "on" and self.checkboxcrs_var.get() == "on":
                        print("This is TFTP > running-config and copy startup > running")
                        print(f"This is TFTP > running-config on: {self.chkbxrunningcfg_var.get()}")
                        print(f"copy running > startup on: {self.checkboxcrs_var.get()}")
                        # Running = copy running-config startup-config
                        copy_command1 = "copy running-config startup-config"
                        output1 = net_connect.send_command_timing(copy_command1)
                        self.grpdesetry.insert(INSERT, f"Output1:\n{output1}\n")
                        
                        if "Destination filename" in output1:
                            output1 += net_connect.send_command_timing("\n")
                            self.grpdesetry.insert(INSERT, f"running-config > startup-config output:\n{output1}\n")
                            self.grpdesetry.insert(INSERT, "------------------------------------------------\n")
                            self.grpdesetry.insert(INSERT, f"Request completed: running-config has been copied to the startup-config\n")
                            self.grpdesetry.insert(INSERT, "------------------------------------------------\n")
                        else:
                            print("else - self.checkboxcrs_var.get() Destination filename not found ")
                            print(f"'copy running > startup'=off: {self.checkboxcrs_var.get()} ")
                    
                        self.progstatus.set(0.8)
                        self.update_idletasks()


                    # running config checkbox + copy running > startup
                    print(f"COPY IF~chkbxstartupcfg_var on: {self.chkbxstartupcfg_var.get()}")
                    print(f"COPY IF~checkboxcstrn_var on: {self.checkboxcstrn_var.get()}")
                    if self.chkbxstartupcfg_var.get() == "on" and self.checkboxcstrn_var.get() == "on":
                        print("This is TFTP > startup-config and copy startup > running")
                        print(f"This is TFTP > startup on: {self.chkbxstartupcfg_var.get()}")
                        print(f"copy startup > running on: {self.checkboxcstrn_var.get()}")
                        # Running = copy running-config startup-config
                        copy_command1 = "copy startup-config running-config "
                        output1 = net_connect.send_command_timing(copy_command1)
                        self.grpdesetry.insert(INSERT, f"Output1:\n{output1}\n")
                        
                        if "Destination filename" in output1:
                            output1 += net_connect.send_command_timing("\n")
                            self.grpdesetry.insert(INSERT, f"copy startup-config running-config: COMPLETED\n{output1}\n")
                        else:
                            print("else - self.checkboxcrs_var.get() Destination filename not found ")
                            print(f"'copy running > startup'=off: {self.checkboxcstrn_var.get()} ")

                        self.progstatus.set(0.8)
                        self.update_idletasks()
                    
                    # copy startup-config > running-config
                    print(f"COPY IF~chkbxstartupcfg_var on: {self.chkbxstartupcfg_var.get()}")
                    print(f"COPY IF~checkboxcstrn_var on: {self.checkboxcstrn_var.get()}")
                    if self.chkbxstartupcfg_var.get() == "on" and self.checkboxcstrn_var.get() == "on":
                        print("This is TFTP > startup and copy startup > running")
                        print(f"This is TFTP > startup on: {self.chkbxstartupcfg_var.get()}")
                        print(f"copy startup > running on: {self.checkboxcstrn_var.get()}")
                        # Running = copy running-config startup-config
                        copy_command1 = "copy startup-config running-config"
                        output1 = net_connect.send_command_timing(copy_command1)
                        self.grpdesetry.insert(INSERT, f"Output1:\n{output1}\n")
                        
                        if "Destination filename" in output1:
                            output1 += net_connect.send_command_timing("\n")
                            self.grpdesetry.insert(INSERT, f"startup-config > running-config output:\n{output1}\n")
                            self.grpdesetry.insert(INSERT, "------------------------------------------------\n")
                            self.grpdesetry.insert(INSERT, f"Request completed: startup-config has been copied to the running-config.\n")
                            self.grpdesetry.insert(INSERT, "------------------------------------------------\n")

                        else:
                            print("else - self.checkboxcrs_var.get() Destination filename not found ")
                            print(f"'copy running > startup'=off: {self.checkboxcrs_var.get()} ")

                        self.progstatus.set(0.8)
                        self.update_idletasks()
    
                elif self.checkboxrepl_var.get() == "on":
                    # CONFIGURE REPLACE checkboxrepl
                    print("CONFIGURE REPLACE")
                    print(f"CONFIGURE REPLACE self.checkboxrepl_var: {self.checkboxrepl_var.get()}")
                    self.tftpuploadcmd.set(f"configure replace tftp://{tftpserver}{tftprootdir}/{self.filename.get()} force") 
                    self.grpdesetry.insert(INSERT, f"6) Sending the command: {self.tftpuploadcmd.get()}\n")
                    print(f"Try 1b (uploadtftp)copy_command:{self.tftpuploadcmd.get()}")
                    output = net_connect.send_command_timing(self.tftpuploadcmd.get())
                    self.grpdesetry.insert(INSERT, f"7) Output:\n{output}\n")

                    if self.checkboxcrs_var.get() == "on":
                        # Running = copy running-config startup-config
                        copy_command1 = "copy running-config startup-config"
                        output1 = net_connect.send_command_timing(copy_command1)
                        self.grpdesetry.insert(INSERT, f"8) Output1:\n{output1}\n")
                        
                        if "Destination filename" in output1:
                            output1 += net_connect.send_command_timing("\n")
                            self.grpdesetry.insert(INSERT, f"8a) Capturing running-config > startup-config output:\n{output1}\n")
                            self.grpdesetry.insert(INSERT, "------------------------\n")
                            self.grpdesetry.insert(INSERT, f"Request completed: running-config has been copied to the startup-config\n")
                            self.grpdesetry.insert(INSERT, "------------------------")
                        else:
                            print("else - self.checkboxcrs_var.get() Destination filename not found ")
                            print(f"'copy running > startup'=off: {self.checkboxcrs_var.get()} ")
                    else:
                        print("self.checkboxcrs_var.get() == 'off' so not running 'copy running > startup'")
                        print(f"'copy running > startup'=off: {self.checkboxcrs_var.get()} ")

                    self.progstatus.set(0.8)
                    self.update_idletasks()
            
            
            # netmiko.exceptions.NetmikoTimeoutException
            except NetMikoAuthenticationException as AuthException:
                print("326 Authentication Error %s" % AuthException)
                self.grpdesetry.insert(INSERT, f"***--- {self.returnedname.get()} ({self.returnedip.get()}) ---*** \n Authentication Error \n {AuthException}")
            except NetMikoTimeoutException as TimeoutException:
                #raise ConnectError(TimeoutException.args[0])
                print("331 No Valid Connections Error SSH connection 'USERNAME ?': %s" % TimeoutException)
                self.grpdesetry.insert(INSERT, f"Timeout Exception Error ***--- {self.returnedname.get()} ({self.returnedip.get()}) ---*** \n {TimeoutException}")
            except SSHException as sshexception:
                print("An netmiko sshexception Error %s" % sshexception)
                self.grpdesetry.insert(INSERT, f"SSHException Error ***--- {self.returnedname.get()} ({self.returnedip.get()}) ---*** \n {sshexception}")
            except Exception as unknown_error:
                print("339 An netmiko Unknown Error %s"+ str(unknown_error))
                self.grpdesetry.insert(INSERT, f"Unknown Error ***--- {self.returnedname.get()} ({self.returnedip.get()}) ---*** \n  \n {unknown_error}")
            except:
                print(f"An netmiko function exception occurred in tftpconfig(uploadtftp.py) {self.returnedname.get()},{self.returnedip.get()}")
                self.grpdesetry.insert(INSERT, f"5) An undefined error occured in the try block on: {self.returnedname.get()},{self.returnedip.get()}\n")
            
            finally:
                print("Doing net_connect.disconnect")
                net_connect.disconnect
                self.grpdesetry.insert(INSERT, "------------------------------------------------\n")
                self.grpdesetry.insert(INSERT, f"Request completed: {self.returnedname.get()} has a new {self.running_startup_var.get()}\n")
                self.grpdesetry.insert(INSERT, "------------------------------------------------")
                print(f"The file I want to delete is:\n{tftprootdir}/{self.filename.get()}")
                # Lets delete the file from the staging area
                if os.path.exists(f"{tftprootdir}/{self.filename.get()}"):
                    os.remove(f"{tftprootdir}/{self.filename.get()}")
                    print("File Deleted from TFTP Staging area")
                else:
                    print(f"I couldn't delete the file:\n{tftprootdir}/{self.filename.get()}")

                self.statuslbl.configure(text='Task Completed')
                self.grpdesetry.see('end')
                self.progstatus.set(1)
                self.update_idletasks()



        else:
            print("4e) File is not in the temporary linux directory")
            self.grpdesetry.insert(INSERT, f"File or Location ERROR")
            self.grpdesetry.insert(INSERT, f"1) Expected file:\n {self.filename.get()}\n")
            self.grpdesetry.insert(INSERT, f"2) Expected location:\n {self.tftprootdir.get()}\n")
        

    # this is when you select a file and press set button on file selection.
    def capturefile(self):
        #self.lbxcontenttopindex = 0
        #self.lbxcontenttopindex  = self.lbxcontenttop.curselection()
        
        if self.lbxcontenttop.curselection() == ():
            print("lbxcontenttopindex has no value")
            tk.messagebox.showinfo(title="Selection error", message="Please select a valid file option.", icon='error', parent=self.tftpuploadwindow)
        else:
            print(f"lbxcontenttopindex HAS A VALUE:{self.lbxcontenttop.get(self.lbxcontenttop.curselection())}")

            try:
                print(f"3a) Capture the file selected in txtcontenttop = {self.lbxcontenttop.get(self.lbxcontenttop.curselection())}")
                self.filename.set(self.lbxcontenttop.get(self.lbxcontenttop.curselection()))
                
                print(f"3b) The current working directory is : {os.getcwd()}")

                # WINDOWS
                '''
                self.filedest.set(f"{tftprootdir}\\{self.filename.get()}")
                print(f"Linux tftprootdir = {tftprootdir}")
                print(f"Linux self.filename.get() = {self.filename.get()}")
                print(f"Linux filesdestination self.filedest.get() = {self.filedest.get()}")
                print("========================================================")
                self.filesrc.set(f"{devicesaveddata}\\{self.returnedname.get()}\\{self.filename.get()}")
                print(f"Linux devicesaveddata = {devicesaveddata}")
                print(f"Linux self.returnedname = {self.returnedname.get()}")
                print(f"Linux self.filename.get() = {self.filename.get()}")
                print(f"Linux filesource self.filesrc.get() = {self.filesrc.get()}")
                '''
                
                # LINUX
                self.filedest.set(f"{tftprootdir}/{self.filename.get()}")
                print(f"Linux tftprootdir = {tftprootdir}")
                print(f"Linux self.filename.get() = {self.filename.get()}")
                print(f"Linux filesdestination self.filedest.get() = {self.filedest.get()}")
                print("========================================================")
                self.filesrc.set(f"{devicesaveddata}/{self.returnedname.get()}/{self.filename.get()}")
                print(f"Linux devicesaveddata = {devicesaveddata}")
                print(f"Linux self.returnedname = {self.returnedname.get()}")
                print(f"Linux self.filename.get() = {self.filename.get()}")
                print(f"Linux filesource self.filesrc.get() = {self.filesrc.get()}")
                
                try:
                    # Change elements on the window
                    # self.devname.configure(state='disabled') # disable the label
                    self.selectdevicecbx.configure(fg_color="gray64", border_color='gray40', button_color='gray40', button_hover_color='gray40', state='disabled', text_color_disabled='white')
                    self.lbxcontenttop.configure(selectbackground="gray40", background='gray64')
                    self.lbxcontenttop.bindtags((self, "all")) # disables reselection but keeps selected highlighted 
                    self.btnselect.configure(text="Accepted", state='disabled', fg_color="gray", text_color_disabled="white")
                    self.grpdesetry.config(state=NORMAL)
                    self.grpdesetry.config(background="light yellow")
                    self.btnupload.configure(state='normal', fg_color="green", border_color='dark green')
                    # Copy file to the tftp temp area
                    shutil.copy(self.filesrc.get(), self.filedest.get())
                    self.grpdesetry.insert(INSERT, f"1) The file has been copied to the TFTP staging area: \n {self.filename.get()} \n")
                    print(f"Copied {self.filename.get()}")
                    
                    self.progstatus.set(0.2)
                    #self.statuslbl.configure(font=('arial', 10, 'bold'), text=f'{(self.filename.get()[:26])}...')
                    self.statuslbl.configure(text=f'Ready for uploading')


                except:
                    print("Copy Error to TFTP area")
                    self.grpdesetry.insert(INSERT, f"Problem copying file to staging area: \n {self.filename.get()} \n")
                    self.grpdesetry.insert(INSERT, f"File source: \n {self.filesrc.get()} \n")
                    self.grpdesetry.insert(INSERT, f"File Destination: \n {self.filedest.get()} \n")
            
            except:
                print(f"5a) lbxcontenttop.get is FALSE")
                print(f"3b) The current self.filename.get() : {self.filename.get()}")

    
    def selectdevicecbxcallback(self):
        # EVERYTIME THE COMBOBOX IS CHANGED CLEAR THE 'lbxcontenttop' TEXTBOX
        self.lbxcontenttop.delete(0, 'end')
        
        print("selectdevicecbxcallback")
        print(self.selectdevicecbx.get())
        self.btnselect.configure(fg_color="green", state='normal')
        self.lbxcontenttop.configure(state='normal', background='pale green')

        cbxid = re.findall(r'^\D*(\d+)', self.selectdevicecbx.get())
        cbxidstr = str(cbxid[0])
        # print(f"This is the selected item in the combobox: {cbxidstr}")

        retid1 = 0
        retname1 = ""
        retip1 = ""
        retusername1 = ""
        retpassword1 = ""
        retdescription1 = ""
        rettype1 = ""
        retgolden1 = ""

        retid1, retname1, retip1, retusername1, retpassword1, retdescription1, rettype1, retgolden1 = getdevicefromid(cbxidstr)
            
        self.returnedid.set(retid1)
        self.returnedname.set(retname1)
        self.returnedip.set(retip1)
        self.returnedusername.set(retusername1)
        self.returnedpassword.set(retpassword1)
        self.returneddescription.set(retdescription1)
        self.returnedtype.set(rettype1)
        self.returnedgolden.set(retgolden1)

        print(f"1) uploadtftp.py setdevice (retid1): {self.returnedid.get()}")
        print(f"2) uploadtftp.py setdevice (retname1): {self.returnedname.get()}")
        print(f"3) uploadtftp.py setdevice (retip1): {self.returnedip.get()}")
        print(f"4) uploadtftp.py setdevice (retusername1): {self.returnedusername.get()}")
        print(f"5) uploadtftp.py setdevice (retpassword1): {self.returnedpassword.get()}")
        print(f"6) uploadtftp.py setdevice (retdescription1): {self.returneddescription.get()}")
        print(f"7) uploadtftp.py setdevice (rettype1): {self.returnedtype.get()}")
        print(f"8) uploadtftp.py setdevice (retgolden1): {self.returnedgolden.get()}")

        try:
            print(f"1) uploadtftp.py 'setdevice' The current working directory is : {os.getcwd()}")
            devicedir = os.path.join(devicesaveddata, retname1)
            os.chdir(devicedir)
            print(f"1a) The current working directory is : {os.getcwd()}")
            print(f"1b) The devicesaveddata is : {devicesaveddata}")
            filesindir = [f for f in os.listdir('.') if os.path.isfile(f)]
            for f in filesindir:
                print(f)
                self.lbxcontenttop.insert('end', f)
        except:
            print(f"1ca) directory is : {os.getcwd()}")
            print(f"1db) The devicesaveddata is : {devicesaveddata}")
            print(f"1ec) directory could not be found : {devicedir}")
            self.lbxcontenttop.insert('end', "A problem exists")
            self.lbxcontenttop.insert('end', "The device directory could not be found")
            self.lbxcontenttop.insert('end', f"{devicedir}")
        
        self.progstatus.set(0.1)
        self.statuslbl.configure(text=f'{self.returnedname.get()} selected')


    # Copy(merge) config checkbox
    def checkboxcopy_event(self):
        if self.checkboxcopy.get() == "on":
            print(f"checkboxcopy_event value 'Copy(merge) config' (on): {self.checkboxcopy.get()}")
            print(f"checkboxrepl_event value 'Replace running config' (off): {self.checkboxrepl.get()}")
            self.checkboxrepl.deselect() # deactivate 'Replace running config'
            # Activate 'running-config'
            self.chkbxrunningcfg.configure(border_color="dark slate gray", state="normal")
            # Activate 'startup-config'
            self.chkbxstartupcfg.configure(border_color="dark slate gray", state="normal")
            # Activate 'copy startup > running'
            self.checkboxcstrn.configure(border_color="dark slate gray", state="normal")
            self.checkboxcrs.deselect() #'copy  running > startup'
            self.checkboxcrs.configure(border_color="dark slate gray", state="normal")
            self.checkboxcstrn.deselect() #'copy startup > running'
            self.checkboxcstrn.configure(border_color="gray", state="disabled")
            # select running config by default
            self.chkbxrunningcfg.select()
        
        else:
            print(f"checkboxcopy_event value 'Copy(merge) config' (off): {self.checkboxcopy.get()}")
            print(f"checkboxrepl_event value 'Replace running config' (on): {self.checkboxrepl.get()}")
            self.checkboxrepl.select() # activate 'Replace running config'
            #  deactivate & disable 'running-config'
            self.chkbxrunningcfg.deselect()
            self.chkbxrunningcfg.configure(border_color="gray", state="disabled")
            #  deactivate & disable 'startup-config'
            self.chkbxstartupcfg.deselect()
            self.chkbxstartupcfg.configure(border_color="gray", state="disabled")
            # deselect and disable 'copy startup > running'
            self.checkboxcstrn.deselect() #'copy startup > running'
            self.checkboxcstrn.configure(border_color="gray", state="disabled")
            self.checkboxcrs.deselect() #'copy  running > startup'
            self.checkboxcrs.configure(border_color="dark slate gray", state="normal")


    # Replace running config Checkbox
    def checkboxrepl_event(self):
        self.checkboxcopy.deselect()

        if self.checkboxrepl.get() == "on":
            print(f"checkboxrepl_event value 'Replace running config' (on): {self.checkboxrepl.get()}")
            self.checkboxcrs.deselect() #'copy  running > startup'
            self.checkboxcrs.configure(border_color="dark slate gray", state="normal") #'copy  running > startup'
            self.checkboxcstrn.deselect()
            self.checkboxcstrn.configure(border_color="gray", state="disabled") #'copy startup > running'
            #  deactivate & disable 'running-config'
            self.chkbxrunningcfg.deselect()
            self.chkbxrunningcfg.configure(border_color="gray", state="disabled")
            #  deactivate & disable 'startup-config'
            self.chkbxstartupcfg.deselect()
            self.chkbxstartupcfg.configure(border_color="gray", state="disabled")
            
            self.running_startup_var.set("running-config")
        
        else:
            # If Replace running config off
            print(f"checkboxrepl_event value 'Replace running config' (off): {self.checkboxrepl.get()}")
            self.checkboxcopy.select() # Copy(merge) config selected
            # running-config active and selected
            self.chkbxrunningcfg.configure(border_color="dark slate gray", state="normal")
            self.chkbxrunningcfg.select()
            #  activate 'startup-config'
            self.chkbxstartupcfg.deselect()
            self.chkbxstartupcfg.configure(border_color="dark slate gray", state="normal")
            # deactivate 'copy running > startup'
            self.checkboxcrs.deselect()

    # runnning-config checkbox
    def chkbxrunningcfg_event(self):
        #self.checkboxcstrn.deselect()
        if self.chkbxrunningcfg_var.get()=="on":
            print(f"chkbxrunningcfg_event (on): {self.chkbxrunningcfg_var.get()}")
            self.chkbxstartupcfg.deselect()
            self.checkboxcrs.configure(border_color="dark slate gray", state="normal") #'copy  running > startup'
            self.checkboxcstrn.deselect()
            self.checkboxcstrn.configure(border_color="gray", state="disabled") #'copy startup > running'
        
        else:
            print(f"chkbxrunningcfg_event (off): {self.chkbxrunningcfg_var.get()}")
            self.chkbxstartupcfg.select()
            # 'copy running > startup' deselect and disable
            self.checkboxcrs.deselect() #'copy  running > startup'
            self.checkboxcrs.configure(border_color="gray", state="disabled") #'copy  running > startup'
            # copy startup > running deselect and enable
            self.checkboxcstrn.deselect()
            self.checkboxcstrn.configure(border_color="dark slate gray", state="normal") #'copy startup > running'
    
    # startup-config Checkbox
    def chkbxstartupcfg_event(self):
        self.checkboxcstrn.deselect()

        if self.chkbxrunningcfg_var.get() == "on":
            print(f"chkbxstartupcfg_event (on): {self.chkbxstartupcfg_var.get()}")
            self.chkbxrunningcfg.deselect() # 'running-config' = deselect
            self.checkboxcrs.deselect() # 'copy running > startup' deselect
            self.checkboxcstrn.configure(border_color="dark slate gray", state="normal") # copy 'startup > running' enable
            self.checkboxcrs.configure(border_color="gray", state="disabled") # 'copy running > startup' disable
        
        else:
            print(f"chkbxstartupcfg_event 'startup-config' (off): {self.chkbxstartupcfg.get()}")
            # running-config select
            self.chkbxrunningcfg.select()
            # 'copy running > startup' deselect & normalise
            self.checkboxcrs.deselect()
            self.checkboxcrs.configure(border_color="dark slate gray", state="normal")
            self.checkboxcstrn.configure(border_color="gray", state="disabled") # copy 'startup > running' disabled
    
    # 'copy running > startup' Checkbox
    def checkboxcrs_event(self):
        print(f"checkboxcrs_event ('copy running > startup'): {self.checkboxcrs_var.get()}")
        

    #  'copy running > startup'
    def checkboxcstrn_event(self):
        print(f"checkboxcstrn_event: {self.checkboxcstrn_var.get()}")


    self.tftpuploadwindow = tk.Toplevel()
    self.tftpuploadwindow.geometry("600x790+200+200")
    self.tftpuploadwindow.title("TFTP Upload to device")
    self.tftpuploadwindow.configure(background='powder blue')
    self.tftpuploadwindow.resizable(False, False)
    self.tftpuploadwindow.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    # Disable root window when this one is active
    self.tftpuploadwindow.attributes('-topmost', 'true')
    self.tftpuploadwindow.transient(self)
    self.tftpuploadwindow.update_idletasks()
    self.tftpuploadwindow.grab_set()

    self.tftpuploadwindow.grid_columnconfigure(0, weight=1)
    #self.tftpuploadwindow.grid_columnconfigure(0, weight=0)
    #self.tftpuploadwindow.grid_columnconfigure(1, weight=0)
    #self.tftpuploadwindow.grid_columnconfigure(2, weight=2)
    
    # top label
    self.lbltopa = ctk.CTkLabel(self.tftpuploadwindow, text="TFTP Upload config to device", font=('arial', 22, 'bold'))
    self.lbltopa.grid(row=0, column=0,sticky=EW, columnspan=3, pady=(15,10))
    #lbltopb = Label(top, background='powder blue', font='arial 14 bold italic', text=retusername) #textvariable=ipaddress
    #lbltopb.grid(row=0,column=1, sticky='w', pady=10)
    self.separator1 = ttk.Separator(self.tftpuploadwindow, orient='horizontal')
    self.separator1.grid(row=1, sticky=EW, columnspan=3, padx=20, pady=8)

    # Select device combobox
    self.devname = Label(self.tftpuploadwindow, text="Device selection", font=('arial', 14, 'bold'), bg="powder blue", fg='black')
    self.devname.grid(row=2, column=0,sticky=W, padx=(25,5), pady=(10))
    self.selectdevicecbx = ctk.CTkComboBox(self.tftpuploadwindow, width=350, font=('arial', 14, 'bold'), fg_color="pale green", border_color='dark slate gray', button_color='dark slate gray', button_hover_color='green4', dropdown_fg_color='pale green', dropdown_hover_color='PaleGreen3', values=devicesdetails, command=lambda v:selectdevicecbxcallback(self)) # '''values=devicesdetails,'''
    self.selectdevicecbx.set(value="select a device")
    self.selectdevicecbx.grid(row=2, column=1, sticky=E, padx=(10,30), pady=10, columnspan=2)
    
    self.lbxcontenttop = Listbox(self.tftpuploadwindow, height=12, width=180, selectbackground="green4", background='gray70', activestyle=NONE, selectmode=SINGLE, state='disabled')
    self.lbxcontenttop.grid(row=3, column=0, padx=(25, 0), pady=(10), sticky=EW, columnspan=2)
    
    self.btnselect = ctk.CTkButton(self.tftpuploadwindow, text="Select", command=lambda:capturefile(self), width=80, fg_color="gray", hover_color='green4', state='disabled')
    self.btnselect.grid(row=3, column=2, padx=(0,30), pady=10, sticky=SE)
    self.horizspr1 = ttk.Separator(self.tftpuploadwindow, orient='horizontal')
    self.horizspr1.grid(row=5, column=0, sticky=EW, columnspan=3, padx=20, pady=10)
    self.grpdesetry = scrolledtext.ScrolledText(self.tftpuploadwindow, font='arial 12', height=10, width=10, bg="gray70", wrap=WORD, state=DISABLED)
    self.grpdesetry.grid(row=6, column=0, sticky=EW, padx=(25, 30), pady=10, columnspan=3)
    
    # Checkboxes copy/merge config | Replace config | copy run start
    self.checkboxcopy = ctk.CTkCheckBox(self.tftpuploadwindow, text="Copy(merge) config", command=lambda:checkboxcopy_event(self), variable=self.checkboxcopy_var, onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="dark slate gray", border_width=2, text_color_disabled="gray")
    self.checkboxcopy.grid(row=7, column=0, pady=10, sticky=W, padx=(25,0))
    self.checkboxrepl = ctk.CTkCheckBox(self.tftpuploadwindow, text="Replace running config", command=lambda:checkboxrepl_event(self), variable=self.checkboxrepl_var, onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="dark slate gray", border_width=2, text_color_disabled="gray")
    self.checkboxrepl.grid(row=7, column=1, pady=10, sticky=W)
    self.checkboxcrs = ctk.CTkCheckBox(self.tftpuploadwindow, text="copy runnning > startup", command=lambda:checkboxcrs_event(self), variable=self.checkboxcrs_var, onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="dark slate gray", border_width=2, text_color_disabled="gray")
    self.checkboxcrs.grid(row=7, column=2, pady=10, padx=(0,35))
    self.chkbxrunningcfg = ctk.CTkCheckBox(self.tftpuploadwindow, text="running-config", command=lambda:chkbxrunningcfg_event(self), variable=self.chkbxrunningcfg_var, onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="dark slate gray", border_width=2, text_color_disabled="gray")
    self.chkbxrunningcfg.grid(row=8, column=0, pady=10, sticky=W, padx=(25,0))
    self.chkbxstartupcfg = ctk.CTkCheckBox(self.tftpuploadwindow, text="startup-config", command=lambda:chkbxstartupcfg_event(self), variable=self.chkbxstartupcfg_var, onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="dark slate gray", border_width=2, text_color_disabled="gray")
    self.chkbxstartupcfg.grid(row=8, column=1, pady=10, sticky=W)
    self.checkboxcstrn = ctk.CTkCheckBox(self.tftpuploadwindow, text="copy startup > runnning", command=lambda:checkboxcstrn_event(self), variable=self.checkboxcstrn_var, state='disabled', onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.checkboxcstrn.grid(row=8, column=2, pady=10, padx=(0,35))
    self.virtspr1 = ttk.Separator(self.tftpuploadwindow, orient='vertical')
    self.virtspr1.grid(row=7, column=1, sticky='nsw', rowspan=2, pady=(10,5), padx=(180,1))
    
    self.horizspr2 = ttk.Separator(self.tftpuploadwindow, orient='horizontal')
    self.horizspr2.grid(row=9, column=0, sticky=EW, columnspan=3, padx=(20), pady=(10))

    self.btnupload = ctk.CTkButton(self.tftpuploadwindow, text="Upload", command=lambda:tftptorouter(self), state='disabled', width=240, fg_color="gray", font=('arial', 12, 'bold'), hover_color='green4', border_width=2, border_color='gray')
    self.btnupload.grid(row=11, column=0, padx=30, pady=10, sticky=W)
    self.btnclose = ctk.CTkButton(self.tftpuploadwindow, text = "Exit", width=140, fg_color="red3", font=('arial', 12, 'bold'), hover_color='red4', border_width=2, border_color='red4', command=self.tftpuploadwindow.destroy)
    self.btnclose.grid(row=11, column=2,  pady=10, sticky=E, padx=(10,30))
    self.statuslbl = tk.Label(self.tftpuploadwindow, text="Select a device", font=('arial', 12, 'bold'), bg='powder blue', fg='OrangeRed2')
    self.statuslbl.grid(row=11, column=1)

    self.progstatus = ctk.CTkProgressBar(self.tftpuploadwindow, progress_color='green4', border_width=1, border_color='dark slate gray')
    self.progstatus.grid(row=12, column=0, sticky=EW, padx=30, pady=5, columnspan=3)
    self.progstatus.set(0)

uploadtftp.py
jolders/devnetnode
from settings import *
from tkinter import *
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
from PIL import ImageTk, Image
import customtkinter as ctk
import sqlite3
from mysql.connector import Error
import os
from os.path import exists
import json
from connecttodb import getdevicefromid
import shutil
from netmiko import ConnectHandler 
from netmiko.exceptions import NetMikoAuthenticationException
from netmiko.exceptions import NetMikoTimeoutException
from netmiko.exceptions import SSHException
# from netmiko.ssh_exception import NetMikoAuthenticationException
# from netmiko.ssh_exception import NetMikoTimeoutException
# from netmiko.ssh_exception import SSHException


def configuploadtftpgroup(self):

    # Generates the IDs of the members of the group
    self.idsofmembers=[]
    
    # dropdown group selection box and selection in the box is the var value
    self.tftpgrpseleccbx = []
    self.tftpgrpcombxstr_var = ctk.StringVar(self)

    # holding the switch value on / off
    self.grpnameswitch_var = ctk.StringVar(self)

    # tftp particulars
    self.tftpselectedid=StringVar(self)
    self.setbackuptypevar=StringVar(self) # Bakup type (startup, running)
    self.setfileidtypevar=StringVar(self) # File identifier (weekly, golden ?)
    self.filename = StringVar(self)
    self.filedest = StringVar(self)
    self.filesrc = StringVar(self)
    self.tftprootdir = StringVar(self)
    self.tftprootdir.set(tftprootdir) # C:\\Shared\\NetDevApp3\\cisco12ct\\devicedata\\tftp_temp

    # Values of the checkboxes
    self.checkboxcopygrp_var = ctk.StringVar(value="off") # Copy(merge) config
    self.checkboxreplgrp_var = ctk.StringVar(value="off")# Replace running config
    self.checkboxcrsgrp_var = ctk.StringVar(value="off") # 'copy running > startup'
    self.checkboxcstrngrp_var = ctk.StringVar(value="off") # copy startup > running
    self.chkbxrunningcfgrp_var = ctk.StringVar(value="off") # running-config
    self.chkbxstartupcfgrp_var = ctk.StringVar(value="off") # startup-config 

    self.tftpuploadcmd = ctk.StringVar(self)
    
    # Evaluate the value of the checkbox to determin running or startup
    self.running_startup_vargrp = StringVar(self)

    #self.returnedid = IntVar(self)
    #self.returnedname = StringVar(self)
    #self.returnedip = StringVar(self)
    #self.returnedusername = StringVar(self)
    #self.returnedpassword = StringVar(self)
    #self.returneddescription = StringVar(self)
    #self.returnedtype = StringVar(self)
    #self.returnedgolden = StringVar(self)
    
    print("configuploadtftpgroup")
    self.tftpuploadgroupw = tk.Toplevel()
    self.tftpuploadgroupw.geometry("730x820+200+200")
    self.tftpuploadgroupw.title("TFTP Upload to group")
    self.tftpuploadgroupw.configure(background='powder blue')
    #self.tftpuploadgroupw.resizable(False, False)
    self.tftpuploadgroupw.wm_iconphoto(False, (ImageTk.PhotoImage(Image.open(pythonicon))))

    # Disable root window when this one is active
    self.tftpuploadgroupw.attributes('-topmost', 'true')
    self.tftpuploadgroupw.transient(self)
    self.tftpuploadgroupw.update_idletasks()
    self.tftpuploadgroupw.grab_set()

    #self.tftpuploadgroupw.grid_columnconfigure(0, weight=1)
    #self.tftpuploadgroupw.grid_columnconfigure(0, weight=0)
    #self.tftpuploadgroupw.grid_columnconfigure(1, weight=0)
    #self.tftpuploadgroupw.grid_columnconfigure(2, weight=2)
    
    def tftpgrpgobtn(self):
        self.grpnotetxtbx.configure(state='normal', bg="light goldenrod yellow")
        self.grpnotetxtbx.insert('end', "This is a list of id members of the group\n")
        self.grpnotetxtbx.insert('end', f"{self.idsofmembers}\n")
        # Disable the GoGroup button
        self.tftpgrpgo.configure(state='disabled', fg_color='gray', border_color='gray')
        # Disable the switch
        self.grpnameswitch.configure(state='disabled', fg_color='gray50', button_color='gray30', border_color='gray30', progress_color='gray', text_color='gray20')
        # Disable checkbox selections
        self.checkboxcopygrp.select()
        self.checkboxcopygrp.configure(state='disabled', fg_color="gray", border_color="gray") # Copy(merge) config
        self.chkbxrunningcfgrp.select()
        self.chkbxrunningcfgrp.configure(state='disabled', fg_color="gray", border_color="gray") # running-config
        self.checkboxreplgrp.deselect()
        self.checkboxreplgrp.configure(state='disabled', fg_color="gray", border_color="gray") # Replace running-config
        self.chkbxstartupcfgrp.deselect()
        self.chkbxstartupcfgrp.configure(state='disabled', fg_color="gray", border_color="gray") # startup-config
        self.checkboxcrsgrp.deselect()
        self.checkboxcrsgrp.configure(state='disabled', fg_color="gray", border_color="gray") # copy running > startup
        self.checkboxcstrngrp.deselect()
        self.checkboxcstrngrp.configure(state='disabled', fg_color="gray", border_color="gray") # copy startup > running


        count = 0
        for i in self.idsofmembers:
            count += 1
            print(f"{i}")
            retid = 0
            retname = ""
            retip = ""
            retusername = ""
            retpassword = ""
            retdescription = ""
            rettype = ""
            retgolden = ""
            
            retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(i)
            print(f"{retid}"+f"{retname}"+f"{retip}"+f"{retusername}"+f"{retpassword}"+f"{retdescription}"+f"{rettype}"+f"{retgolden}")
            self.grpnotetxtbx.insert('end', f"id:{retid}\nhostname:{retname}\nip:{retip}\nfile:{retgolden}\n")

            self.progstatus1.set(0.3)
            self.update_idletasks()

            os.chdir(self.tftprootdir.get())
            print(f"4b) Upload to Device name: {retname}")
            print(f"4c) The current working directory is : {os.getcwd()}")
            # What is the file name
            print(f"4d) File name to look for is: {retgolden}")

            self.grpnotetxtbx.insert(INSERT, f"2) Looking for file in the TFTP staging area:\n{retgolden}\n")
            
            # Evaluate the value of the checkbox to determin running or startup
            # # self.running_startup_var = StringVar()
            if self.chkbxstartupcfgrp_var.get() == "on":
                self.running_startup_vargrp.set("startup-config")
                print(f"chkbxrunningcfgrp_var value = {self.chkbxrunningcfgrp_var.get()}")
            elif self.chkbxrunningcfgrp_var.get() == "on":
                self.running_startup_vargrp.set("running-config")
                print(f"chkbxstartupcfgrp_var value = {self.chkbxstartupcfgrp_var.get()}")
            else:
                print("uploadtftp.py(tftprouter) error occured getting checbxstartupcfg_var or checbxrunningcfg_var")

            if os.path.isfile(retgolden):
                print("4)d File exist in the TFTP staging directory: so lets go")

                self.tftpstatuslbl.configure(text=f'Running task on: {retname}') # font=('arial', 12, 'bold'), 
                self.progstatuslbl.configure(text=f"id:{retid} ({retname})")
                self.update_idletasks()

                cisco_device = {
                    'device_type': f'{rettype}',
                    'host': f'{retip}',
                    'username': f'{retusername}',
                    'password': f'{retpassword}',
                    'port': 22,
                    'secret': f'{retpassword}',
                    'verbose': True}
                
                self.grpnotetxtbx.insert(INSERT, f"3) Initiating Connection to : {retname}\n")

                conn = ConnectHandler(**cisco_device)
                conn_prompt = conn.find_prompt()
                if '>' in conn_prompt: conn.enable()
                output = conn.send_command(f"\n") # what do enter into the devices command line to accept a tftp upload ?
                print(f"Checking the config mode: {conn.check_config_mode()}")
                self.grpnotetxtbx.insert(INSERT, f"4) The config mode on the device is: {conn.check_config_mode()}\n")

                self.progstatus1.set(0.4)
                self.update_idletasks()

                try:
                    name = f"device{str(1)}"
                    net_connect = ConnectHandler(**cisco_device)
                    print("Try 1a")
                    net_connect.enable()
                    hostname = net_connect.find_prompt()[:-1]
                    print(f"The hosname found on the device is: {hostname}")
                    self.grpnotetxtbx.insert(INSERT, f"5) The hostname on the device is: {hostname}\n")
                    
                    print(f"The value of checkboxcopygrp_var is: {self.checkboxcopygrp_var.get()}")
                    
                    self.tftpstatuslbl.configure(font=('arial', 12, 'bold'), text='Task in progress')
                    self.progstatus1.set(0.5)
                    self.update_idletasks()

                    if self.checkboxcopygrp_var.get() == "on": # Copy(merge) config
                        # Option1 Copy tftp > 'running-config' Option2 Copy tftp > 'startup-config'
                        # option changes due to value in running_startup_var
                        print("Option2 'startup-config checkboxcopygrp_var")
                        self.tftpuploadcmd.set(f"copy tftp://{tftpserver}{tftprootdir}/{retgolden} {self.running_startup_vargrp.get()}")
                        self.grpnotetxtbx.insert(INSERT, f"6) Sending the command: {self.tftpuploadcmd.get()}\n")
                        print(f"Try 1b (uploadtftp)copy_command:{self.tftpuploadcmd.get()}")
                        output = net_connect.send_command_timing(self.tftpuploadcmd.get())
                        if "Address or name" in output:
                            output += net_connect.send_command_timing(f"{tftpserver}\n")
                            print("Try 2")
                        if "Source filename" in output:
                            output += net_connect.send_command_timing(f"{retgolden}\n")
                            print("Try 3")
                            net_connect.disconnect
                        if "Destination filename" in output:
                            output += net_connect.send_command_timing(f"\n")
                            print("Try 4 ---- I THINK IT DID IT")
                            self.grpnotetxtbx.insert(INSERT, f"7) Running the command on the device.\n")
                        
                        self.progstatus1.set(0.7)
                        self.update_idletasks()

                        # 'running-config' checkbox on + 'copy running > startup' checkbox on
                        print(f"COPY IF~chkbxrunningcfgrp_var on: {self.chkbxrunningcfgrp_var.get()}")
                        print(f"COPY IF~checkboxcrsgrp_var on: {self.checkboxcrsgrp_var.get()}")
                        if self.chkbxrunningcfgrp_var.get() == "on" and self.checkboxcrsgrp_var.get() == "on":
                            print("This is TFTP > running-config and copy startup > running")
                            print(f"This is TFTP > running-config on: {self.chkbxrunningcfgrp_var.get()}")
                            print(f"copy running > startup on: {self.checkboxcrsgrp_var.get()}")
                            # Running = copy running-config startup-config
                            copy_command1 = "copy running-config startup-config"
                            output1 = net_connect.send_command_timing(copy_command1)
                            self.grpnotetxtbx.insert(INSERT, f"Output1:\n{output1}\n")
                        
                            if "Destination filename" in output1:
                                output1 += net_connect.send_command_timing("\n")
                                self.grpnotetxtbx.insert(INSERT, f"copy running-config startup-config: COMPLETED\n{output1}\n")
                            else:
                                print("else - self.checkboxcrs_var.get() Destination filename not found ")
                                print(f"'copy running > startup'=off: {self.checkboxcrsgrp_var.get()} ")
                    
                            self.progstatus1.set(0.8)
                            self.update_idletasks()

                        # Copy(merge) config = on
                        # running config checkbox + copy running > startup
                        print(f"COPY IF~chkbxstartupcfgrp_var on: {self.chkbxstartupcfgrp_var.get()}")
                        print(f"COPY IF~checkboxcstrngrp_var on: {self.checkboxcstrngrp_var.get()}")
                        if self.chkbxstartupcfgrp_var.get() == "on" and self.checkboxcstrngrp_var.get() == "on":
                            print("This is TFTP > startup-config and copy startup > running")
                            print(f"This is TFTP > startup on: {self.chkbxstartupcfgrp_var.get()}")
                            print(f"copy startup > running on: {self.checkboxcstrngrp_var.get()}")
                            # Running = copy running-config startup-config
                            copy_command1 = "copy startup-config running-config "
                            output1 = net_connect.send_command_timing(copy_command1)
                            self.grpnotetxtbx.insert(INSERT, f"Output1:\n{output1}\n")
                            
                            if "Destination filename" in output1:
                                output1 += net_connect.send_command_timing("\n")
                                self.grpnotetxtbx.insert(INSERT, f"copy startup-config running-config: COMPLETED\n{output1}\n")
                            
                            else:
                                print("else - self.checkboxcrs_var.get() Destination filename not found ")
                                print(f"'copy running > startup'=off: {self.checkboxcstrngrp_var.get()} ")
                            
                            self.progstatus1.set(0.8)
                            self.update_idletasks()
                        
                        # copy startup-config > running-config
                        print(f"COPY IF~chkbxstartupcfg_var on: {self.chkbxstartupcfgrp_var.get()}")
                        print(f"COPY IF~checkboxcstrn_var on: {self.checkboxcstrngrp_var.get()}")
                        if self.chkbxstartupcfgrp_var.get() == "on" and self.checkboxcstrngrp_var.get() == "on":
                            print("This is TFTP > startup and copy startup > running")
                            print(f"This is TFTP > startup on: {self.chkbxstartupcfgrp_var.get()}")
                            print(f"copy startup > running on: {self.checkboxcstrngrp_var.get()}")
                            # Running = copy running-config startup-config
                            copy_command1 = "copy startup-config running-config"
                            output1 = net_connect.send_command_timing(copy_command1)
                            self.grpnotetxtbx.insert(INSERT, f"Output1:\n{output1}\n")
                            
                            if "Destination filename" in output1:
                                output1 += net_connect.send_command_timing("\n")
                                self.grpnotetxtbx.insert(INSERT, f"startup-config > running-config output:\n{output1}\n")
                                self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                                self.grpnotetxtbx.insert(INSERT, f"Request completed: startup-config has been copied to the running-config\n")
                                self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                            else:
                                print("else - self.checkboxcrs_var.get() Destination filename not found ")
                                print(f"'copy running > startup'=off: {self.checkboxcrsgrp_var.get()} ")
                            
                            self.progstatus1.set(0.8)
                            self.grpnotetxtbx.see('end')
                            self.update_idletasks()
                        
                    elif self.checkboxreplgrp_var.get() == "on":
                        # CONFIGURE REPLACE checkboxrepl
                        print("CONFIGURE REPLACE")
                        print(f"CONFIGURE REPLACE self.checkboxreplgrp_var: {self.checkboxreplgrp_var.get()}")
                        self.tftpuploadcmd.set(f"configure replace tftp://{tftpserver}{tftprootdir}/{retgolden} force") 
                        self.grpnotetxtbx.insert(INSERT, f"6) Sending the command: {self.tftpuploadcmd.get()}\n")
                        print(f"Try 1b (uploadtftp)copy_command:{self.tftpuploadcmd.get()}")
                        output = net_connect.send_command_timing(self.tftpuploadcmd.get())
                        self.grpnotetxtbx.insert(INSERT, f"7) Output:\n{output}\n")

                        if self.checkboxcrsgrp_var.get() == "on":
                            # Running = copy running-config startup-config
                            copy_command1 = "copy running-config startup-config"
                            output1 = net_connect.send_command_timing(copy_command1)
                            self.grpnotetxtbx.insert(INSERT, f"8) Output1:\n{output1}\n")
                        
                            if "Destination filename" in output1:
                                output1 += net_connect.send_command_timing("\n")
                                self.grpnotetxtbx.insert(INSERT, f"8a) running-config > startup-config output:\n{output1}\n")
                                self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                                self.grpnotetxtbx.insert(INSERT, f"Request completed: running-config has been copied to the startup-config\n")
                                self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                            else:
                                print("else - self.checkboxcrs_var.get() Destination filename not found ")
                                print(f"'copy running > startup'=off: {self.checkboxcrsgrp_var.get()} ")
                        else:
                            print("self.checkboxcrs_var.get() == 'off' so not running 'copy running > startup'")
                            print(f"'copy running > startup'=off: {self.checkboxcrsgrp_var.get()} ")

                        self.progstatus.set(0.8)
                        self.grpnotetxtbx.see('end')
                        self.update_idletasks()
                
                # Write some more specific exception classes for Netmiko exceptions
                # netmiko.exceptions.NetmikoTimeoutException
                except NetMikoAuthenticationException as AuthException:
                    print("(uploadtftpgroup.py 289): Authentication Error" % AuthException)
                    self.grpnotetxtbx.insert(INSERT, "5) (uploadtftpgroup.py 290) Authentication Error \n"% AuthException)
                    continue
                except NetMikoTimeoutException as TimeoutException:
                    #raise ConnectError(TimeoutException.args[0])
                    print("(uploadtftpgroup.py 293): No Valid Connections Error SSH connection 'USERNAME ?': %s" % TimeoutException)
                    self.grpnotetxtbx.insert(INSERT, "5) (uploadtftpgroup.py 295) TimeoutException Error \n" % TimeoutException)
                    continue
                except SSHException as sshexception:
                    print("An netmiko sshexception Error" % sshexception)
                    self.grpnotetxtbx.insert(INSERT, "5) (uploadtftpgroup.py 299) An netmiko SSHException Error \n" % sshexception)
                    continue
                except Exception as unknown_error:
                    print("An netmiko function exception occurred in tftpconfig(uploadtftpgroup.py)"+ str(unknown_error))
                    self.grpnotetxtbx.insert(INSERT, "5) An error occured in the try block unknown_error\n" % unknown_error)
                    continue

                finally:
                    print("Doing net_connect.disconnect")
                    net_connect.disconnect
                    self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                    self.grpnotetxtbx.insert(INSERT, f"Request was completed {retname} has a new {self.running_startup_vargrp.get()}\n")
                    self.grpnotetxtbx.insert(INSERT, "------------------------------------------------\n")
                    print(f"The file I want to delete is:\n{tftprootdir}/{retgolden}")
                    # Lets delete the file from the staging area
                    if os.path.exists(f"{tftprootdir}/{retgolden}"):
                        os.remove(f"{tftprootdir}/{retgolden}")
                        print("File Deleted from TFTP Staging area")
                    else:
                        print(f"I couldn't delete the file:\n{tftprootdir}/{retgolden}")
                        self.grpnotetxtbx.insert(INSERT, f"9) (finally|else 352) I couldn't delete the file:\n{tftprootdir}/{retgolden}")


                    self.grpnotetxtbx.insert(INSERT, F"-----------------------------End ({retname})-------------------------------\n")
                    self.tftpstatuslbl.configure(text='Task Completed')
                    self.grpnotetxtbx.see('end')
                    self.progstatus1.set(1)
                    self.update_idletasks()

            
            else:
                print("4e) File is not in the tftp directory")
                self.grpnotetxtbx.insert(INSERT, f"File or Location ERROR\n")
                self.grpnotetxtbx.insert(INSERT, f"1) Expected file:\n {retgolden}\n")
                self.grpnotetxtbx.insert(INSERT, f"2) Expected location:\n {self.tftprootdir.get()}\n")
        
        self.grpnotetxtbx.insert(INSERT, f"--------------------Tasks Finished ({self.tftpgrpcombxstr_var.get()})-------------------------------")
        self.grpnotetxtbx.see('end')
        self.grpnotetxtbx.configure(state='disabled', bg="light goldenrod yellow")




    # Populate the Groups into the Combobox selection dropdown
    def populatemembxg(self, *args, **kwargs):
        #self.tftpgrpcombx.configure(state='normal')
        os.chdir(devicegroups)
        #print(f"1a) The current working directory is : {os.getcwd()}")
        #print(f"devicegroups = {devicegroups}")
        items = os.listdir(devicegroups)
        print(f"items = {items}")
        for item in items:
            if os.path.isdir(item):
                #print(f"item = {item}")
                self.tftpgrpseleccbx.append(item)
                #print(f"self.tftpgrpseleccbx = {self.tftpgrpseleccbx}")
        
        self.tftpgrpcombx.configure(values=self.tftpgrpseleccbx)


    def tftpgrpcombxcallback(self):
        print(f"grpseleccbxcallback")
        print("Clear the table if not clear")
        #self.tftpgrptbl.delete(*self.tftpgrptbl.get_children())
        #self.tftpgrptbl.delete(*self.tftpgrptbl.get_children())
        print(f"Whats the group in the dropdown box ?: {self.tftpgrpcombx.get()}")
        #print(f"TryAgain = {self.grpnamecbx.get()}")
        #self.grpconfirmbtn.configure(state='enabled', fg_color='green')
        #self.grploadbtn.configure(state='enabled', fg_color='green')

        # Auto generate the table
        self.grpnameswitch.configure(state='normal', fg_color='pale green', button_color='dark slate gray', border_color='dark slate gray', text_color='black')
        tftpgrpcbxselc(self)


        
    # Checkbox = Copy(merge) config (uploadtftp = checkboxcopy_event)
    def checkboxcopygrp_event(self):
        print(f"I am checkboxcopygrp_event: {self.checkboxcopygrp.get()}")
        if self.checkboxcopygrp.get() == "on":
            # deactivate 'Replace running config'
            self.checkboxreplgrp.deselect()
            # Activate 'running-config'
            self.chkbxrunningcfgrp.configure(border_color="dark slate gray", state="normal")
            # Activate 'startup-config'
            self.chkbxstartupcfgrp.configure(border_color="dark slate gray", state="normal")
            # Activate 'copy startup > running'
            self.checkboxcstrngrp.configure(border_color="dark slate gray", state="normal")
            #'copy  running > startup'
            self.checkboxcrsgrp.deselect()
            self.checkboxcrsgrp.configure(border_color="dark slate gray", state="normal")
            #'copy startup > running'
            self.checkboxcstrngrp.deselect() 
            self.checkboxcstrngrp.configure(border_color="gray", state="disabled")
            # select running config by default
            self.chkbxrunningcfgrp.select()

        else:
            # activate 'Replace running config'
            self.checkboxreplgrp.select()
            #  deactivate & disable 'running-config'
            self.chkbxrunningcfgrp.deselect()
            self.chkbxrunningcfgrp.configure(border_color="gray", state="disabled")
            #  deactivate & disable 'startup-config'
            self.chkbxstartupcfgrp.deselect()
            self.chkbxstartupcfgrp.configure(border_color="gray", state="disabled")
            # deselect and disable 'copy startup > running'
            self.checkboxcstrngrp.deselect()
            self.checkboxcstrngrp.configure(border_color="gray", state="disabled")
            #'copy  running > startup'
            self.checkboxcrsgrp.deselect() 
            self.checkboxcrsgrp.configure(border_color="dark slate gray", state="normal")

    # Checkbox = Replace running config
    def checkboxreplgrp_event(self):
        print(f"I am checkboxreplgrp_event: {self.checkboxreplgrp.get()}")
        self.checkboxcopygrp.deselect()
        #self.checkboxreplgrp.configure(fg_color="green4")

        if self.checkboxreplgrp.get() == "on":
            #'copy  running > startup'
            self.checkboxcrsgrp.deselect()
            self.checkboxcrsgrp.configure(border_color="dark slate gray", state="normal")
            #'copy startup > running'
            self.checkboxcstrngrp.deselect()
            self.checkboxcstrngrp.configure(border_color="gray", state="disabled")
            #  deactivate & disable 'running-config'
            self.chkbxrunningcfgrp.deselect()
            self.chkbxrunningcfgrp.configure(border_color="gray", state="disabled")
            #  deactivate & disable 'startup-config'
            self.chkbxstartupcfgrp.deselect()
            self.chkbxstartupcfgrp.configure(border_color="gray", state="disabled")

            self.running_startup_vargrp.set("running-config")
        else:
            # Copy(merge) config selected
            self.checkboxcopygrp.select()
            # running-config active and selected
            self.chkbxrunningcfgrp.configure(border_color="dark slate gray", state="normal")
            self.chkbxrunningcfgrp.select()
            #  activate 'startup-config'
            self.chkbxstartupcfgrp.deselect()
            self.chkbxstartupcfgrp.configure(border_color="dark slate gray", state="normal")
            # deactivate 'copy running > startup'
            self.checkboxcrsgrp.deselect()
    
    # Checkbox = running config #chkbxrunningcfg
    def chkbxrunningcfgrp_event(self):
        print(f"I am chkbxrunningcfgrp_event: {self.chkbxrunningcfgrp.get()}")
        if self.chkbxrunningcfgrp.get()=="on":
            self.chkbxstartupcfgrp.deselect()
            #'copy  running > startup'
            self.checkboxcrsgrp.configure(border_color="dark slate gray", state="normal") 
            # copy startup > running
            self.checkboxcstrngrp.deselect()
            self.checkboxcstrngrp.configure(border_color="gray", state="disabled") #'copy startup > running'

        else:
            self.chkbxstartupcfgrp.select()
            # 'copy running > startup' deselect and disable
            self.checkboxcrsgrp.deselect() #'copy  running > startup'
            self.checkboxcrsgrp.configure(border_color="gray", state="disabled") #'copy  running > startup'
            # copy startup > running deselect and enable
            self.checkboxcstrngrp.deselect()
            self.checkboxcstrngrp.configure(border_color="dark slate gray", state="normal") #'copy startup > running'

    # Checkbox = startup config
    def chkbxstartupcfgrp_event(self):
        print(f"I am chkbxstartupcfgrp_event: {self.chkbxstartupcfgrp.get()}")
        self.checkboxcstrngrp.deselect()

        if self.chkbxstartupcfgrp.get() == "on":
            # 'running-config' = deselect
            self.chkbxrunningcfgrp.deselect()
            # 'copy running > startup' deselect
            self.checkboxcrsgrp.deselect()
            # copy 'startup > running' enable
            self.checkboxcstrngrp.configure(border_color="dark slate gray", state="normal")
            # 'copy running > startup' disable
            self.checkboxcrsgrp.configure(border_color="gray", state="disabled")
        
        else:
            # running-config select
            self.chkbxrunningcfgrp.select()
            # 'copy running > startup' deselect & normalise
            self.checkboxcrsgrp.deselect()
            self.checkboxcrsgrp.configure(border_color="dark slate gray", state="normal")
            # copy 'startup > running' disabled
            self.checkboxcstrngrp.configure(border_color="gray", state="disabled") 

    # Checkbox = copy runnning > startup #checkboxcrs
    def checkboxcrsgrp_event(self):
        print(f"I am checkboxcrsgrp_event: {self.checkboxcrsgrp.get()}")
    
    # copy startup > runnning
    def checkboxcstrngrp_event(self):
        print(f"I am checkboxcstrngrp_event: {self.checkboxcstrngrp.get()}")

    def tftpgrpcbxselc(self):
        self.tftpgrptbl.delete(*self.tftpgrptbl.get_children())
        print("set button")
        print(f"set button value = {self.tftpgrpcombx.get()}")

        print("Go off and populate the table with the groups details")

        # Get the members of the selected group
        # Get into the right folder
        groudevicepath = os.path.join(devicegroups, self.tftpgrpcombx.get())
        # load the file
        dest_path = os.path.join(groudevicepath, self.tftpgrpcombx.get() + ".json")
        filetoread = open(dest_path)
        inputjsondata = json.load(filetoread)
        print(f"Addbutton filetoread = {filetoread}")
        print(f"Addbutton inputjsondata = {inputjsondata}")

        # change members selection
        #inputjsondata['members'] = fvmemselec
        print(f"1) Data in members: {inputjsondata['members']}")

        self.idsofmembers=inputjsondata['members']
        print(f"idsofmembers: {self.idsofmembers}")
        
        count = 0

        for i in self.idsofmembers:
            count += 1
            print(f"{i}")
            retid = 0
            retname = ""
            retip = ""
            retusername = ""
            retpassword = ""
            retdescription = ""
            rettype = ""
            retgolden = ""
            
            retid, retname, retip, retusername, retpassword, retdescription, rettype, retgolden = getdevicefromid(i)
            print(f"{retid}"+f"{retname}"+f"{retip}"+f"{retusername}"+f"{retpassword}"+f"{retdescription}"+f"{rettype}"+f"{retgolden}")
            self.tftpgrptbl.insert(parent='', index='end', iid=count, text=count, values=(f'{retid}',f'{retname}',f'{retip}',f'{retgolden}'))
        
        # count the number of devices in the group display in status label
        self.tftpstatuslbl.configure(text="Number of devices in group: " + str(count))

    def tftpgrpconfirm(self):
        print("tftpgrpconfirm(self): Pressed / switched")
  
        '''
        # This section gets the treeview data and copys the file to the sataging tftp area
        devlist = []
        devlistcount = 0
        #print(f"devlist 0 1 = {devlist[0][1]}")
        for child in self.tftpgrptbl.get_children():
            devlist.append(self.tftpgrptbl.item(child)['values'])

        for file in devlist:
            print(f"devlistcount: {str(devlistcount)}")
            # file source
            self.filesrc.set(f"{devicesaveddata}/{devlist[devlistcount][1]}/{devlist[devlistcount][3]}")
            # file destination
            self.filedest.set(f"{tftprootdir}/{devlist[devlistcount][3]}")
            print(f"self.filesrc: {self.filesrc.get()}")
            print(f"self.filedest: {self.filedest.get()}")
            # copy file
            
            try:
                shutil.copy(self.filesrc.get(), self.filedest.get())
                self.grpnotetxtbx.insert(INSERT, f"{devlistcount}) File ready: {devlist[devlistcount][3]}\n")

            except:
                print(f"File copy problem {devlist[devlistcount][3]}")
                self.grpnotetxtbx.insert(INSERT, f"{devlistcount}) Problem preparing file: {devlist[devlistcount][3]}\n")


            devlistcount +=1
        '''
        



        if self.grpnameswitch_var.get() == 'on':
            print(f"571 - self.grpnameswitch_var = on : {self.grpnameswitch_var.get()}")

            # Disable Grop selection box
            self.tftpgrpcombx.configure(state='disabled', fg_color='gray80', border_color='gray', button_color='gray')

            # Treeview gray out
            self.tftpgrptbl.configure(style="dbgray.Treeview")

            # Enable checkbox selections
            self.checkboxcopygrp.configure(state='normal', fg_color="green4", border_color="dark slate gray") # Copy(merge) config
            self.chkbxrunningcfgrp.configure(state='normal', fg_color="green4", border_color="dark slate gray") # running-config
            self.checkboxreplgrp.configure(state='normal', fg_color="green4", border_color="dark slate gray") # Replace running-config
            self.chkbxstartupcfgrp.configure(state='normal', fg_color="green4", border_color="dark slate gray") # startup-config
            self.checkboxcrsgrp.configure(state='normal', fg_color="green4", border_color="dark slate gray") # copy running > startup
            self.checkboxcstrngrp.configure(fg_color="green4", border_color="gray") # copy startup > running
            

            # Go Button
            self.tftpgrpgo.configure(state='normal', fg_color="green4", border_color='dark green')

            
            # Output box
            self.grpnotetxtbx.configure(state='normal', bg="light goldenrod yellow")
            devlist = []
            devlistcount = 0
            #print(f"devlist 0 1 = {devlist[0][1]}")
            for child in self.tftpgrptbl.get_children():
                devlist.append(self.tftpgrptbl.item(child)['values'])
            
            for file in devlist:
                print(f"devlistcount: {str(devlistcount)}")
                # file source
                self.filesrc.set(f"{devicesaveddata}/{devlist[devlistcount][1]}/{devlist[devlistcount][3]}")
                # file destination
                self.filedest.set(f"{tftprootdir}/{devlist[devlistcount][3]}")
                print(f"self.filesrc: {self.filesrc.get()}")
                print(f"self.filedest: {self.filedest.get()}")
                # copy file

                try:
                    shutil.copy(self.filesrc.get(), self.filedest.get())
                    self.grpnotetxtbx.insert(INSERT, f"{devlistcount+1}) File ready: {devlist[devlistcount][3]}\n")
                
                except:
                    print(f"File copy problem {devlist[devlistcount][3]}")
                    self.grpnotetxtbx.insert(INSERT, f"{devlistcount+1}) Problem preparing file: {devlist[devlistcount][3]}\n")
                    
                devlistcount +=1
            self.grpnotetxtbx.configure(state='disabled')


        if self.grpnameswitch_var.get() == 'off':
            print(f"self.grpnameswitch_var = off : {self.grpnameswitch_var.get()}")

            # Enable group selection combobox
            self.tftpgrpcombx.configure(state='normal', fg_color='yellow2', border_color='dark slate gray', button_color='dark slate gray') 

            # Treeview selection normal
            self.tftpgrptbl.configure(style="db.Treeview")

            # clear the output scroolbar
            self.grpnotetxtbx.configure(state='normal')
            self.grpnotetxtbx.delete('1.0', END)
            self.grpnotetxtbx.configure(state='disabled', bg="gray60")

            # DO I NEED TO CLEAR THE TFTP TEMPORARY AREA ?
            #print("594 - DO I NEED TO CLEAR THE TFTP TEMPORARY AREA ? = yes")
            if len(os.listdir(filesource)) > 0:
                print(" 617 - (uploadtftpgroup) Clearing the TFTP AREA of any existing files")
                # shutil.rmtree(filesource)
                for f in os.listdir(filesource):
                    os.remove(os.path.join(filesource, f))
            
            # Disable checkbox selections
            self.checkboxcopygrp.select()
            self.checkboxcopygrp.configure(state='disabled', fg_color="gray", border_color="gray") # Copy(merge) config
            self.chkbxrunningcfgrp.select()
            self.chkbxrunningcfgrp.configure(state='disabled', fg_color="gray", border_color="gray") # running-config
            self.checkboxreplgrp.deselect()
            self.checkboxreplgrp.configure(state='disabled', fg_color="gray", border_color="gray") # Replace running-config
            self.chkbxstartupcfgrp.deselect()
            self.chkbxstartupcfgrp.configure(state='disabled', fg_color="gray", border_color="gray") # startup-config
            self.checkboxcrsgrp.deselect()
            self.checkboxcrsgrp.configure(state='disabled', fg_color="gray", border_color="gray") # copy running > startup
            self.checkboxcstrngrp.deselect()
            self.checkboxcstrngrp.configure(state='disabled', fg_color="gray", border_color="gray") # copy startup > running

            # Deactivate Go Button
            self.tftpgrpgo.configure(state='disabled', fg_color="gray", border_color='gray')


    # Top Frame
    self.topframea = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color='powder blue', height=30) # OUTPUT DATA AREA
    self.topframea.pack(fill='both', expand=False, side='top')
    self.topframea.pack_propagate(True)
    self.lbltopa = ctk.CTkLabel(self.topframea, text="Upload a configuration to a group of devices.", font=('arial', 22, 'bold'))
    self.lbltopa.pack(pady=(15,7), side='top')
    self.separator1 = ttk.Separator(self.topframea, orient='horizontal')
    self.separator1.pack(padx=20, pady=(5,10), fill='x', expand=True, side='top')

    self.grpname = ctk.CTkLabel(self.topframea, text="Group name", font=('arial', 18, 'bold'))
    self.grpname.pack(anchor='w', padx=(30,30), pady=(1,20), side='left')
    self.tftpgrpcombx = ctk.CTkComboBox(self.topframea, state='normal', command=lambda v: tftpgrpcombxcallback(self), variable=self.tftpgrpcombxstr_var, width=270, height=30, fg_color='yellow2', font=('arial', 14, 'bold'), border_color='dark slate gray', button_color='dark slate gray', dropdown_fg_color='khaki', border_width=2, dropdown_hover_color='khaki3', dropdown_font=('arial', 14, 'bold')) # , values=memseleccbxvar
    self.tftpgrpcombx.pack(padx=(60,0), pady=(1,20), side='left')
    self.tftpgrpcombx.set("Group Selection")
    #self.grpconfirmbtn = ctk.CTkButton(self.topframea, text="Confirm", state='disabled', width=100, fg_color="gray", hover_color='green4', command=lambda:tftpgrpconfirm(self)) # 
    #self.grpconfirmbtn.pack(padx=(0,30), pady=(1,20), side='right')
    #self.grploadbtn = ctk.CTkButton(self.topframea, text="Load", state='disabled', width=100, fg_color="gray", hover_color='green4', command=lambda:tftpgrpcbxselc(self)) # 
    #self.grploadbtn.pack(padx=(0,10), pady=(1,20), side='right')
    self.grpnameswitch = ctk.CTkSwitch(self.topframea, text="Unlock | Lock", onvalue="on", offvalue="off", switch_width=40, switch_height=20, fg_color='gray50', button_color='gray30', border_width=2, border_color='gray30', progress_color='yellow', text_color='gray20', command=lambda:tftpgrpconfirm(self), variable=self.grpnameswitch_var) # command=grpnameswitch_event, variable=self.grpnameswitch_var,
    self.grpnameswitch.pack(padx=(0,30), pady=(1,20), side='right')
    self.grpnameswitch.configure(state='disabled')


    # Center Frame
    self.centerframe = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color="powder blue") # OUTPUT DATA AREA
    self.centerframe.pack(fill='both', expand=True, side='top')
    self.topframea.pack_propagate(True)
    
    # Output textscroll
    self.grpnotetxtbx = scrolledtext.ScrolledText(master=self.centerframe, font='arial 12', height=10,  bg="gray60", wrap=WORD, state='disabled')
    self.grpnotetxtbx.pack(side="bottom", expand='True', fill='x', padx=(20,20))
    
    # Treeview to show group (id, name, ip, filename)
    self.tftpgrptbl = ttk.Treeview(self.centerframe, selectmode='none', height=10, style="db.Treeview") #height=19, 
    self.tftpgrptbl.pack(side="left", expand='True', fill='both', padx=(20,0))  #fill='x', expand=True, anchor='n', side='left'
    # Adding a vertical scrollbar to Treeview widget
    self.tftpgrptblscroll = ttk.Scrollbar(self.centerframe, orient ="vertical", command=self.tftpgrptbl.yview)
    self.tftpgrptbl.configure(yscrollcommand=self.tftpgrptblscroll.set)
    self.tftpgrptblscroll.pack(side='right', expand='False', fill='y', anchor=E, padx=(0,20))
    # Treeview config
    self.tftpgrptbl["columns"] = ("1", "2", "3", "4")
    self.tftpgrptbl['show'] = 'headings'
    self.tftpgrptbl.column("1", width=50, anchor='center')
    self.tftpgrptbl.column("2", width=100, anchor='center')
    self.tftpgrptbl.column("3", width=100, anchor='center')
    self.tftpgrptbl.column("4", width=400, anchor='e')
    self.tftpgrptbl.heading("1", text="id")
    self.tftpgrptbl.heading("2", text="hostname")
    self.tftpgrptbl.heading("3", text="ip")
    self.tftpgrptbl.heading("4", text="File")

    # Bottom Frame
    self.bottomframe1 = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color="powder blue") # OUTPUT DATA AREA
    self.bottomframe1.pack(fill='x')
    #self.bottomframe1.pack_propagate(False)

    self.separator2 = ttk.Separator(master=self.bottomframe1, orient='horizontal')
    self.separator2.pack(padx=20, pady=(4,5), fill='x', expand=True, side='top')

    self.checkboxcopygrp = ctk.CTkCheckBox(self.bottomframe1, state='disabled', command=lambda:checkboxcopygrp_event(self), variable=self.checkboxcopygrp_var, text="Copy(merge) config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.checkboxcopygrp.pack(side='left', anchor='n', pady=(10,10), padx=(60,20))
    self.checkboxreplgrp = ctk.CTkCheckBox(self.bottomframe1, state='disabled', command=lambda:checkboxreplgrp_event(self), variable=self.checkboxreplgrp_var, text="Replace running-config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.checkboxreplgrp.pack(side='left', anchor='n', pady=(10,10), padx=(20,20))
    self.separator3a = ttk.Separator(master=self.bottomframe1, orient='vertical')
    self.separator3a.pack(fill='y', expand=False, side='left', padx=20)
    self.checkboxcrsgrp = ctk.CTkCheckBox(self.bottomframe1, state='disabled',command=lambda:checkboxcrsgrp_event(self), variable=self.checkboxcrsgrp_var, text="copy runnning > startup", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.checkboxcrsgrp.pack(side='left', anchor='n', pady=(10,10), padx=(40,20))
    
    self.bottomframe2 = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color="powder blue") # OUTPUT DATA AREA
    self.bottomframe2.pack(fill='x', ipadx=10)
    #self.bottomframe2.pack_propagate(False)
    self.chkbxrunningcfgrp = ctk.CTkCheckBox(self.bottomframe2, state='disabled', command=lambda:chkbxrunningcfgrp_event(self), variable=self.chkbxrunningcfgrp_var, text="running-config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.chkbxrunningcfgrp.pack(side='left', anchor='n', pady=(10,10), padx=(60,20))
    self.chkbxstartupcfgrp = ctk.CTkCheckBox(self.bottomframe2, state='disabled',command=lambda:chkbxstartupcfgrp_event(self), variable=self.chkbxstartupcfgrp_var, text="startup-config", onvalue="on", offvalue="off", fg_color="gray", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.chkbxstartupcfgrp.pack(side='left', anchor='n', pady=(10,10), padx=(50,70))
    self.separator3b = ttk.Separator(master=self.bottomframe2, orient='vertical')
    self.separator3b.pack(fill='y', expand=False, side='left', padx=20, pady=(0,1))
    self.checkboxcstrngrp = ctk.CTkCheckBox(self.bottomframe2, state='disabled', command=lambda:checkboxcstrngrp_event(self), variable=self.checkboxcstrngrp_var, text="copy startup > runnning", onvalue="on", offvalue="off", fg_color="green4", hover_color="dark slate gray", border_color="gray", border_width=2, text_color_disabled="gray")
    self.checkboxcstrngrp.pack(side='left', anchor='n', pady=(10,10), padx=(40,20))
    
    self.bottomframe3 = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color="powder blue") # OUTPUT DATA AREA
    self.bottomframe3.pack(fill='x', ipadx=50, pady=1)
    self.separator4 = ttk.Separator(master=self.bottomframe3, orient='horizontal')
    self.separator4.pack(padx=20, pady=(5,5), fill='x', expand=True, side='top')
    self.tftpgrpgo = ctk.CTkButton(self.bottomframe3, state='disabled', text="GoGroup", width=150, fg_color="gray", hover_color='green4', command=lambda:tftpgrpgobtn(self))
    self.tftpgrpgo.pack(padx=(30,30), pady=(10,5), side='left')
    self.tftpstatuslbl = Label(self.bottomframe3, text="Select a Group ", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    self.tftpstatuslbl.pack(padx=(30,30), pady=(10,5), side='left')
    self.tftpgrpexit = ctk.CTkButton(self.bottomframe3, text="Exit", width=150, fg_color="red3", hover_color='red4', command=self.tftpuploadgroupw.destroy, font=('arial', 12, 'bold'), border_width=2, border_color='red4')
    self.tftpgrpexit.pack(padx=(30,30), pady=(10,5), side='right')
    
    self.bottomframe4 = ctk.CTkFrame(master=self.tftpuploadgroupw, fg_color="powder blue") # OUTPUT DATA AREA
    self.bottomframe4.pack(fill='x', pady=1)
    self.progstatuslbl = Label(self.bottomframe4, text="id:", font=('arial', 14, 'bold'), bg="powder blue", fg='OrangeRed2')
    self.progstatuslbl.pack(padx=(40,30), pady=(5,15), side='left')
    self.progstatus1 = ctk.CTkProgressBar(self.bottomframe4, progress_color='green4', border_width=1, border_color='dark slate gray', width=500)
    self.progstatus1.pack(padx=(40,30), pady=(5,15), side='right')
    self.progstatus1.set(0)

    # Setup the default values for the checkboxes
    self.checkboxcopygrp.select()
    self.chkbxrunningcfgrp.select()
    
    populatemembxg(self)
    
uploadtftpgroup.py
jolders/devnetnode