class RoutingIntStaticRoute(Screen):

    #Function to configure a static route on a device

    def RoutingIntStaticRouteExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            destination_address = self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteIPRouteLayout.ids.IPv4AddressTextInput.text + ' ' + self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteIPRouteLayout.ids.SubnetMaskSpinnerLayout.ids.SubnetMaskSpinner.text
            distance_metric = self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteMetricDistanceLayout.ids.MetricDistanceTextInput.text

            if self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.ids.IPv4AddressTextInput.text == '':
                route_egress = self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.ids.InterfaceTypeSpinnerLayout.ids.InterfaceTypeSpinner.text + ' ' + self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.ids.InterfaceNumberTextInput.text
            else:
                route_egress = self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.ids.IPv4AddressTextInput.text

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            config_commands = [
                "ip route " + destination_address + ' ' + route_egress + ' ' +
                distance_metric
            ]

            net_connect = ConnectHandler(**device)

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text="Successfully configured static route to '[b]" +
                    destination_address +
                    "[/b]' on device with IP address '[b]" +
                    device_ip_address + "[/b]'"),
                size_hint=(0.95, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()

    #Function linked to the Interface Egress radio button to modify the various widgets so that they are only visible and useable when the user wishes to use the interface egress

    def StaticRouteSelectInterfaceEgress(self):

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.disabled = True
        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.disabled = False

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.opacity = 0
        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.opacity = 1

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.ids.IPv4AddressTextInput.text = ''

    #Function linked to the Forward IP Egress radio button to modify the various widgets so that they are only visible and useable when the user wishes to use the forward ip egress

    def StaticRouteSelectIPEgress(self):

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.disabled = False
        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.disabled = True

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardIPLayout.opacity = 1
        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.opacity = 0

        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.ids.InterfaceTypeSpinnerLayout.text = 'Interface Type'
        self.ids._Routing_Int_Static_Route_Layout_.ids.RoutingIntStaticRouteForwardInterfaceLayout.ids.InterfaceNumberTextInput.text = ''
Beispiel #2
0
class NetMonSpanConf(Screen):

    #Function to configure SPAN on a switcing device

    def NetMonSpanConfExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            session_ID = self.ids._Net_Mon_Span_Conf_Layout_.ids.NetMonSpanConfSessionNoLayout.ids.SPANSessionIDTextInput.text
            source_port = self.ids._Net_Mon_Span_Conf_Layout_.ids.NetMonSpanConfSourcePortLayout.ids.InterfaceTypeSpinner.text + ' ' + self.ids._Net_Mon_Span_Conf_Layout_.ids.NetMonSpanConfSourcePortLayout.ids.InterfaceNumberTextInput.text
            destination_port = self.ids._Net_Mon_Span_Conf_Layout_.ids.NetMonSpanConfDestinationPortLayout.ids.InterfaceTypeSpinner.text + ' ' + self.ids._Net_Mon_Span_Conf_Layout_.ids.NetMonSpanConfDestinationPortLayout.ids.InterfaceNumberTextInput.text

            source_command = 'monitor session ' + session_ID + ' source interface ' + source_port
            destionation_command = 'monitor session ' + session_ID + ' destination interface ' + destination_port

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            config_commands = [source_command, destionation_command]

            net_connect = ConnectHandler(**device)

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text="Successfully configured SPAN with session ID '[b]" +
                    session_ID + "[/b]' on device with IP address '[b]" +
                    device_ip_address + "[/b]'"),
                size_hint=(0.8, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()
class RoutingIntDefaultGateway(Screen):

    #Function to configure a default gateway on a device

    def RoutingIntDefaultGatewayExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            default_gateway = self.ids._Routing_Int_Default_Gateway_Layout_.ids.IPv4AddressTextInput.text

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            config_commands = ["ip default-gateway " + default_gateway]

            net_connect = ConnectHandler(**device)

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text="Successfully configured default gateway as '[b]" +
                    default_gateway + "[/b]' on device with IP address '[b]" +
                    device_ip_address + "[/b]'"),
                size_hint=(0.85, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()
Beispiel #4
0
class SecurityConfLocalUsernameDatabase(Screen):

    #Function to add a new user account to a device

    def SecurityConfLocalUsernameDatabaseExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            privilege_level = self.ids._Security_Conf_Local_Username_Database_Layout_.ids.SecurityConfLocalUsernameDatabasePrivilegeLayout.ids.PrivilegeLevelSpinner.text
            new_username = self.ids._Security_Conf_Local_Username_Database_Layout_.ids.SecurityConfLocalUsernameDatabaseUserAndPassLayout.ids.UsernameTextInput.text
            new_password = self.ids._Security_Conf_Local_Username_Database_Layout_.ids.SecurityConfLocalUsernameDatabaseUserAndPassLayout.ids.PasswordTextInput.text

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            priv_check = self.ids._Security_Conf_Local_Username_Database_Layout_.ids.SecurityConfLocalUsernameDatabasePrivilegeLayout.ids.PrivilegeLevelSpinner.text
            secret_check = self.ids._Security_Conf_Local_Username_Database_Layout_.ids.SecurityConfLocalUsernameDatabaseSecretLayout.ids.SecretPasswordTrue.active

            if secret_check == True and priv_check != 'No Privilege Required':  #Priv and Secret
                config_commands = [
                    "username " + new_username + " privilege " +
                    privilege_level + " secret " + new_password
                ]
            elif priv_check != 'No Privilege Required':  #Priv and password
                config_commands = [
                    "username " + new_username + " privilege " +
                    privilege_level + " password " + new_password
                ]
            elif secret_check == True:  #Secret
                config_commands = [
                    "username " + new_username + " secret " + new_password
                ]
            else:
                config_commands = [
                    "username " + new_username + " password " + new_password
                ]  #Standard

            net_connect = ConnectHandler(**device)

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(
                title='',
                content=Label(markup=True,
                              text="Successfully added new user '[b]" +
                              new_username +
                              "[/b]' to device with IP address '[b]" +
                              device_ip_address + "[/b]'"),
                size_hint=(0.8, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()
class IntConfEthernetInt(Screen):        
    

    #Function to make configuration changes to a ethernet interface

    def IntConfEthernetIntExecute(self):
        

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            #Define the three potential commands as empty variables

            description_command = ''
            duplex_command = ''
            bandwidth_command = ''

            interface = self.ids._Int_Conf_Ethernet_Int_Layout_.ids.InterfaceSelectionLayout.ids.InterfaceTypeSpinnerLayout.ids.InterfaceTypeSpinner.text + ' ' +  self.ids._Int_Conf_Ethernet_Int_Layout_.ids.InterfaceSelectionLayout.ids.InterfaceNumberTextInput.text
            interface_command = "interface " + interface


            #If statement to check if user has selected Transport Method checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.DescriptionCheckbox.active == True:

                description_command = 'description ' + self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.ids.EthernetIntDescriptionTextInput.text
            
            else:
                pass



            #If statement to check if user has selected Duplex checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.DuplexCheckbox.active == True:
            

                #If statement for handling if a user does not change duplex type or - It will default to Auto

                if self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.ids.DuplexTypeSpinner.text == 'Duplex Type':
                    duplex_type = 'Auto'
                else:
                    duplex_type = self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.ids.DuplexTypeSpinner.text

                duplex_command = 'duplex ' + duplex_type #Creates the final duplex command

            else:
                pass

            #If statement to check if user has selected Bandwidth checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.BandwidthCheckbox.active == True:
            
                bandwidth_command = 'bandwidth ' + self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.ids.BandwidthTextInput.text
            else:
                pass



            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open() 
                return #Exit from the function


            #If statement to ensure user has entered a username or password
            if App.get_running_app().device_username == '' or App.get_running_app().device_password == '':

                Factory.NoUserOrPassPopup().open() 
                return #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password


            device = { 
              'device_type': 'cisco_ios', 
              'ip': device_ip_address, 
              'username': device_username, 
              'password': device_password, 
              } 

        
            config_commands = [interface_command, description_command, duplex_command, bandwidth_command]
        

            net_connect = ConnectHandler(**device) 

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(title='', content=Label(markup = True, text="Successfully configured interface '[b]" +  interface + "[/b]' of device with IP address '[b]" + device_ip_address + "[/b]'"), size_hint =(0.8, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()


    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()




    #Function linked to the Description checkbox to modify the Description widgets so that they are visible when checked and disabled and hidden from view when unchecked, the text input will have it's value reset
    def IntConfEthernetIntDescriptionSelect(self):

        if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.DescriptionCheckbox.active == True:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.opacity = 1
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.disabled = False

        else:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.opacity = 0
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.disabled = True

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDescriptionLayout.ids.EthernetIntDescriptionTextInput.text = ''




    #Function linked to the Duplex checkbox to modify the Duplex Type widgets so that they are visible when checked and disabled and hidden from view when unchecked, the Spinner input will have it's value reset
    def IntConfEthernetIntDuplexSelect(self):

        if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.DuplexCheckbox.active == True:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.opacity = 1
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.disabled = False

        else:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.opacity = 0
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.disabled = True

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntDuplexLayout.ids.DuplexTypeSpinner.text = 'Duplex Type'
            



    #Function linked to the Bandwidth checkbox to modify the Bandwidth widgets so that they are visible when checked and disabled and hidden from view when unchecked, the text inputs will have it's value reset
    def IntConfEthernetIntBandwidthSelect(self):
        

        if self.ids._Int_Conf_Ethernet_Int_Function_Select_.ids.BandwidthCheckbox.active == True:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.opacity = 1
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.disabled = False


        else:

            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.opacity = 0
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.disabled = True
            
            self.ids._Int_Conf_Ethernet_Int_Layout_.ids.IntConfEthernetIntBandwidthLayout.ids.BandwidthTextInput.text = ''
Beispiel #6
0
class SecurityConfAuxVtyConLines(Screen):

    #Function to configure a the Aux, Con or Vty lines on a device

    def SecurityConfAuxVtyConLinesExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            #Define the three potential commands as empty variables

            transport_command = ''
            login_command = ''
            exec_timeout_command = ''

            #Else if to find out which line the user wishes to configure

            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.ConTrue.active == True:
                line_to_configure = 'Console'
            elif self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.AuxTrue.active == True:
                line_to_configure = 'Aux'
            else:
                line_to_configure = 'Vty'

            #Else if to find out the line_range the user wishes to configure, if console or auxiliary is the line to configure set line to 0

            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.ConTrue.active == True or self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.AuxTrue.active == True:
                line_range = '0'
            else:
                start_line_range = self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.ids.LineRangeStartTextInput.text
                end_line_range = self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.ids.LineRangeEndTextInput.text

                line_range = start_line_range + ' ' + end_line_range  #Define the line_range variable from reading user input from the two text inputs

            line_command = "line " + line_to_configure + ' ' + line_range  #Create a variable to store the command to enter the line to improve ease of reading further down

            #If statement to check if user has selected Transport Method checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.TransportMethodCheckbox.active == True:

                #Creates the variable for input/output dependent on user choice
                transport_type = self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.text  # Defines wheter the user wants to configure input or output transport method

                #If statement for handling if a user does not change method 1 - It will default to ssh

                if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo1Spinner.text == 'Method 1':
                    transport_method1 = 'SSH'
                else:
                    transport_method1 = self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo1Spinner.text

                #If statement for handling if a user does not change method 2 or if N/A was selected. Or if a user has entered 'none' as the value in method 1 - It will default to blank

                if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo2Spinner.text == 'Method 2' or self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo2Spinner.text == 'N/A' or transport_method1 == 'none':
                    transport_method2 = ''
                else:
                    transport_method2 = self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo2Spinner.text

                #Combines the three variables to create the final command

                transport_command = 'transport ' + transport_type + ' ' + transport_method1 + ' ' + transport_method2  # Creates the final Transport Command

            else:
                pass

            #If statement to check if user has selected Login Type checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.LoginTypeCheckbox.active == True:

                #If statement to check if user has selected to login using the local user database or a custom password and set the login_type_command variable accordingly
                if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.ids.LoginLocalTrue.active == True:
                    login_command = 'login local'
                else:
                    login_command = 'password ' + self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.ids.LineLoginPasswordTextInput.text

            else:
                pass

            #If statement to check if user has selected Exec Timeout checkbox, if so the command will be created and inserted into the variable. Else the variable will be left blank

            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.ExecTimeoutCheckbox.active == True:

                exec_timeout_command = 'exec-timeout ' + self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.ids.LineExecTimeoutMinutesTextInput.text + ' ' + self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.ids.LineExecTimeoutSecondsTextInput.text

            else:
                pass

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            config_commands = [
                line_command, transport_command, login_command,
                exec_timeout_command
            ]

            net_connect = ConnectHandler(**device)

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #If statement to check if VTY was the selected function, and then display the text with a line range, else it will display it as line 0
            if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.VtyTrue.active == True:

                #Create and display a popup to inform the user of the successful configuration
                popup = Popup(
                    title='',
                    content=Label(markup=True,
                                  text="Successfully configured '[b]" +
                                  line_to_configure + "[/b]' Lines '[b]" +
                                  start_line_range + " - " + end_line_range +
                                  "[/b]' on device with IP address '[b]" +
                                  device_ip_address + "[/b]'"),
                    size_hint=(0.8, 0.3))
                popup.open()

            else:

                #Create and display a popup to inform the user of the successful configuration
                popup = Popup(
                    title='',
                    content=Label(markup=True,
                                  text="Successfully configured '[b]" +
                                  line_to_configure + "[/b]' Line '[b]" +
                                  line_range +
                                  "[/b]' on device with IP address '[b]" +
                                  device_ip_address + "[/b]'"),
                    size_hint=(0.8, 0.3))
                popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()

    #Function linked to the Console checkbox to modify the various widgets so that only commands that can be performed on the Console line can be set - Remove and reset the Line Range text inputs as Console only allows for line 0 and modifiying which transport methods are available
    def SecurityConfAuxVtyConLinesConSelect(self):

        if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.ConTrue.active == True:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.text = 'Output'
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.values = ''

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo1Spinner.values = 'SSH', 'Telnet', 'all', 'none'

        else:
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.text = 'Input'
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.values = 'Output', 'Input'

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo1Spinner.values = 'SSH', 'Telnet', 'rlogin', 'all', 'none'

    #Function linked to the Aux checkbox to modify the various widgets so that only commands that can be performed on the Console line can be set - Remove and reset the Line Range text inputs as Aux only allows for line 0
    def SecurityConfAuxVtyConLinesVtySelect(self):

        if self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesSelectLineLayout.ids.VtyTrue.active == True:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.opacity = 1
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.disabled = False

        else:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.opacity = 0
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.disabled = True

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.ids.LineRangeStartTextInput.text = ''
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLineRangeLayout.ids.LineRangeEndTextInput.text = ''

    #Function linked to the Transport checkbox to modify the Transport Method widgets so that they are visible when checked and disabled and hidden from view when unchecked, the spinners will have their values reset
    def SecurityConfAuxVtyConLinesTransportSelect(self):

        if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.TransportMethodCheckbox.active == True:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.opacity = 1
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.disabled = False

        else:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.opacity = 0
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.disabled = True

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportInputOutputSpinner.text = 'Output'
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo1Spinner.text = 'Method 1'
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesTransportOptionsLayout.ids.TransportMethodNo2Spinner.text = 'Method 2'

    #Function linked to the Login checkbox to modify the Login Type widgets so that they are visible when checked and disabled and hidden from view when unchecked, the text input will have it's value reset
    def SecurityConfAuxVtyConLinesLoginTypeSelect(self):

        if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.LoginTypeCheckbox.active == True:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.opacity = 1
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.disabled = False

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.ids.LineLoginPasswordTextInput.disabled = True  #Specifically disable password text input until user selects that they want to use password entry

        else:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.opacity = 0
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.disabled = True

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.ids.LoginLocalTrue.active = True
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesLoginOptionsLayout.ids.LineLoginPasswordTextInput.text = ''

    #Function linked to the ExecTimeout checkbox to modify the Exec-Timeout widgets so that they are visible when checked and disabled and hidden from view when unchecked, the text inputs will have their value reset
    def SecurityConfAuxVtyConLinesExecTimeoutSelect(self):

        if self.ids._Security_Conf_Aux_Vty_Con_Lines_Function_Select_.ids.ExecTimeoutCheckbox.active == True:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.opacity = 1
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.disabled = False

        else:

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.opacity = 0
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.disabled = True

            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.ids.LineExecTimeoutMinutesTextInput.text = ''
            self.ids._Security_Conf_Aux_Vty_Con_Lines_Layout_.ids.SecurityConfAuxVtyConLinesExecTimeoutOptionsLayout.ids.LineExecTimeoutSecondsTextInput.text = ''
class DeviceInfoPollAndExtract(Screen):

    #This function will allow for certain information to be extracted from a device and displayed on screen or saved to a file

    def DeviceInfoPollAndExtractExecute(self):

        #Try statement to ensure that any errors connecting and reading the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            selected_storage_directory = App.get_running_app(
            ).selected_storage_directory  #Create a local variable using the value in the global property selected_storage_directory. This will allow the script to create and store files in the directory set by the user

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            net_connect = ConnectHandler(**device)

            #Create variables to store the state of the data information checkboxes, this will allow them to be easily refrenced in the below if statements. This has been done to improve readablity
            extract_hostname_selection = self.ids._Device_Info_Poll_And_Extract_Info_Select_.ids.HostnameInfoCheckbox.active
            extract_device_type_selection = self.ids._Device_Info_Poll_And_Extract_Info_Select_.ids.DeviceTypeInfoCheckbox.active
            extract_ios_version_selection = self.ids._Device_Info_Poll_And_Extract_Info_Select_.ids.IOSVersionInfoCheckbox.active
            extract_domain_selection = self.ids._Device_Info_Poll_And_Extract_Info_Select_.ids.DomainInfoCheckbox.active
            extract_uptime_selection = self.ids._Device_Info_Poll_And_Extract_Info_Select_.ids.UptimeInfoCheckbox.active

            #If statement to see if user selected Hostname checkbox, if so it will call the ExtractHostname function

            if extract_hostname_selection == True:

                output = net_connect.send_command(
                    "show version | include (uptime is)")

                hostname = self.SearchHostname(output)

            else:  #Set hostname to N/A if Hostname is not required

                hostname = 'N/A'

            #If statement to see if user selected Device Type checkbox, if so it will call the ExtractHostname function

            if extract_device_type_selection == True:

                output = net_connect.send_command(
                    "show version | include (bytes of memory)")

                device_type = self.SearchDeviceType(output)

            else:  #Set device_type to N/A if Device Type is not required

                device_type = 'N/A'

            #If statement to see if user selected IOS Version checkbox, if so it will call the ExtractHostname function

            if extract_ios_version_selection == True:

                output = net_connect.send_command(
                    "show version | include (, Version)")

                ios_version = self.SearchIOSVersion(output)

            else:  #Set ios_version to N/A if IOS Version is not required

                ios_version = 'N/A'

            #If statement to see if user selected Domain checkbox, if so it will call the ExtractHostname function

            if extract_domain_selection == True:

                output = net_connect.send_command(
                    "show run | include domain name")

                domain_name = self.SearchDomain(output)

            else:  #Set domain_name to N/A if Domain Name is not required

                domain_name = 'N/A'

            #If statement to see if user selected UpTime checkbox, if so it will call the ExtractHostname function

            if extract_uptime_selection == True:

                output = net_connect.send_command(
                    "show version | include (uptime is)")

                uptime = self.SearchUptime(output)

            else:  #Set uptime to N/A if Uptime is not required

                uptime = 'N/A'

            #If statement to check if user requested for the output to be stored locally, if so it will store the output in a txt file. If not it is passed and nothing is done

            if self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.StoreLocalCheckbox.active == True:

                poll_info_parent_directory = selected_storage_directory + '\\Outputs\\PollDeviceOutput\\'  #Create a variable of the absolute path of where the parent directory for output of data capture will be stored
                poll_info_individual_directory = selected_storage_directory + '\\Outputs\\PollDeviceOutput\\' + device_ip_address  #Create a variable of the absolute path of where the all output files for devices with the same hostname will be stored

                #If statement to check if user has entered an output name, if not the current time and date will be used
                if self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.text == '':

                    file_name = datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p")

                else:

                    file_name = self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.text  #Create a variable for the desired file name

                poll_info_output_file = poll_info_individual_directory + "/" + file_name + '.txt'  #Create a variable for the name and location of the file to be saved. It will be stored as a txt file

                if not os.path.exists(
                        poll_info_parent_directory
                ):  #Check for existence of the parent poll directory - one to store all individual directories - and if not there create it, this is done using the os.path function
                    os.makedirs(poll_info_parent_directory)
                else:
                    pass

                if not os.path.exists(
                        poll_info_individual_directory
                ):  #Check for existence of the individual poll directory - One to store the current output - and if not there create it, this is done using the os.path function
                    os.makedirs(poll_info_individual_directory)
                else:
                    pass

                #Create a list variable to store the outputs required requested by the user, this will then be written into the file specified by the user
                poll_info_output_store_local = [
                    'Hostname: ' + hostname, 'Device Type: ' + device_type,
                    'IOS Version: ' + ios_version, 'Domain: ' + domain_name,
                    'Uptime: ' + uptime
                ]

                with open(poll_info_output_file, 'w') as f:

                    f.write(
                        '\n'.join(poll_info_output_store_local) + '\n'
                    )  #Write each entry of poll_info_output_store_local list in the specified file on a seperate line

                f.close()

            else:
                pass

            #The following two will update the label to display the IP address of the device that was polled. As well as the text input to display the requested info or input 'N/A' if the user did not request the info
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.PolledDeviceInfoLabel.text = 'Device Information for IP Address - [b]' + device_ip_address + '[/b]'
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.PolledDeviceInfoOutput.text = 'Hostname: ' + hostname + ' \nDevice Type: ' + device_type + '\nIOS Version: ' + ios_version + '\nDomain: ' + domain_name + '\nUptime: ' + uptime

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()

    #Function linked to the Local Store checkbox to modify the various widgets so that they are only visible and useable when the user wishes to store the output from device polling
    def DeviceInfoPollAndExtractStoreLocalSelect(self):

        if self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.StoreLocalCheckbox.active == True:

            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameLabel.opacity = 1
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameLabel.disabled = False

            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.opacity = 1
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.disabled = False

        else:

            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameLabel.opacity = 0
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameLabel.disabled = True

            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.opacity = 0
            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.disabled = True

            self.ids._Device_Info_Poll_And_Extract_Layout_.ids.DeviceInfoPollAndExtractStoreLocalLayout.ids.FileNameTextInput.text = ''

    #Function to search output for the Hostname of a device and return it
    def SearchHostname(self, output):

        if re.search(
                "(.+) uptime is", output
        ) == None:  #If regex search finds no matches in string then set hostname as 'unknown'
            hostname = "N/"
        else:  #Else if regex search finds any matches then do the following
            dev_hostname = re.search(
                "(.+) uptime is", output
            )  #Set variable dev_hostname as the output of regex search on the output from the device
            hostname = dev_hostname.group(
                1
            )  #Set variable hostname as the first match from the regex search
            hostname = str(hostname)  #Convert variable to string

        return hostname  #Return variable hostname to function that called it

    #Function to search output for the IOS Version of a device and return it
    def SearchIOSVersion(self, output):

        if re.search(
                "\), Version (.+),", output
        ) == None:  #If regex search finds no matches in string then set uptime as 'unknown'
            ios_version = "N/A"
        else:  #Else if regex search finds any matches then do the following
            dev_version = re.search(
                "\), Version (.+),", output
            )  #Set variable dev_uptime as the output of regex search on the output from the device
            ios_version = dev_version.group(
                1
            )  #Set variable uptime as the first match from the regex search
            ios_version = str(ios_version)  #Convert variable to string

        return ios_version  #Return variable ios to function that called it

    #Function to search output for the Device Type of a device and return it
    def SearchDeviceType(self, output):

        if re.search(
                "(.+?) (.+?) (.+?) (.+) bytes of memory", output
        ) == None:  #If regex search finds no matches in string then set model as 'unknown'
            device_type = "N/A"
        else:  #Else if regex search finds any matches then do the following
            dev_model = re.search(
                "(.+?) (.+?) (.+) bytes of memory", output
            )  #Set variable dev_model as the output of regex search on the output from the device
            device_type = dev_model.group(1) + ' ' + dev_model.group(
                2
            )  #Set variable model as the first match from the regex search
            device_type = str(device_type)  #Convert variable to string

        return device_type  #Return variable model to function that called it

    #Function to search output for the Domain of a device and return it
    def SearchDomain(self, output):

        if re.search(
                "ip domain name (.+)", output
        ) == None:  #If regex search finds no matches in string then set domain_name as 'unknown'
            domain_name = "N/A"
        else:  #Else if regex search finds any matches then do the following
            dev_domain = re.search(
                "ip domain name (.+)", output
            )  #Set variable dev_domain as the output of regex search on the output from the device
            domain_name = dev_domain.group(
                1)  #Set variable ios as the first match from the regex search
            domain_name = str(domain_name)  #Convert variable to string

        return domain_name  #Return variable domain_name to function that called it

    #Function to search output for the Uptime of a device and return it
    def SearchUptime(self, output):

        if re.search(
                "uptime is (.+)", output
        ) == None:  #If regex search finds no matches in string then set uptime as 'unknown'
            uptime = "N/A"
        else:  #Else if regex search finds any matches then do the following
            dev_uptime = re.search(
                "uptime is (.+)", output
            )  #Set variable dev_uptime as the output of regex search on the output from the device
            uptime = dev_uptime.group(
                1
            )  #Set variable uptime as the first match from the regex search
            uptime = str(uptime)  #Convert variable to string

        return uptime  #Return variable uptime to function that called it
class DeviceInfoChangeControlUploadConf(Screen):

    #This function will upload a configuration file to a network device to make a list of configuration changes at once

    def DeviceInfoChangeControlUploadConfExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            net_connect = ConnectHandler(**device)

            selected_conf_file = str(
                self.ids._Device_Info_Change_Control_Upload_Conf_Layout_.ids.
                FileChooser.selection[0])

            with open(
                    selected_conf_file, 'r'
            ) as f:  #user_path is the variable i defined which takes the path of file(configuration file) as raw input from the user.
                config_commands = f.readlines()

            net_connect.send_config_set(config_commands)

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            #Create and display a popup to inform the user of the successful configuration
            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text=
                    "Successfully uploaded configuration file to device with IP address '[b]"
                    + device_ip_address + "[/b]'"),
                size_hint=(0.7, 0.3))
            popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()
class DeviceInfoChangeControlSaveConf(Screen):
    def DeviceInfoChangeControlSaveConfExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._IPv4_Target_Device_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            net_connect = ConnectHandler(
                **device
            )  #Connect to the device using the credentials and IP address

            if self.ids._Device_Info_Change_Control_Save_Conf_Layout_.ids.RunningTrue.active == True:

                self.SaveDeviceRunConf(net_connect)

            else:

                self.SaveDeviceStartupConf(net_connect)

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()

    #Function to save a device running configuration to a file

    def SaveDeviceRunConf(self, net_connect):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            selected_storage_directory = App.get_running_app(
            ).selected_storage_directory  #Create a local variable using the value in the global property selected_storage_directory. This will allow the script to create and store files in the directory set by the user

            output = net_connect.send_command(
                "show version | include (uptime is)")

            hostname = DeviceInfoPollAndExtract.SearchHostname(
                self, output
            )  #Sets hostname as output from search_hostname function - This function is designed to find and return the hostname of a device using regex

            date = datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p")

            run_conf_parent_directory = selected_storage_directory + '\\Outputs\\SaveConfOutput\\'  #Create a variable of the absolute path of where the parent directory for output of data capture will be stored
            run_conf_individual_directory = selected_storage_directory + '\\Outputs\\SaveConfOutput\\' + hostname + '\\RunningConf'  #Create a variable of the absolute path of where the running configurations files for devices with the same hostname will be stored

            run_conf_output_file = run_conf_individual_directory + "/" + hostname + '_' + date + '_RunningConfiguration.txt'  #Create a variable for the name and location of the file to be saved. It will be stored as a txt file

            if not os.path.exists(
                    run_conf_parent_directory
            ):  #Check for existence of the parent poll directory - one to store all individual directories - and if not there create it, this is done using the os.path function
                os.makedirs(run_conf_parent_directory)
            else:
                pass

            if not os.path.exists(
                    run_conf_individual_directory
            ):  #Check for existence of the individual poll directory - One to store the current output - and if not there create it, this is done using the os.path function
                os.makedirs(run_conf_individual_directory)
            else:
                pass

            #show and save running conf of routing device

            net_connect.send_command('skip-page-display')

            output_save_conf = net_connect.send_command('show running-config')

            file = open(
                run_conf_output_file, 'w'
            )  #Set variable file_run to create/Open new/exisitng file and allow it to be written to

            file.write(
                output_save_conf
            )  #Write output from running config from device into the file

            file.close()  #Close file

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text=
                    "Successfully saved the '[b]Running Configuration[/b]' of device with hostname '[b]"
                    + hostname + "[/b]'"),
                size_hint=(0.8, 0.3))
            popup.open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to save a device startup configuration to a file

    def SaveDeviceStartupConf(self, net_connect):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            selected_storage_directory = App.get_running_app(
            ).selected_storage_directory  #Create a local variable using the value in the global property selected_storage_directory. This will allow the script to create and store files in the directory set by the user

            output = net_connect.send_command(
                "show version | include (uptime is)")

            hostname = DeviceInfoPollAndExtract.SearchHostname(
                self, output
            )  #Sets hostname as output from search_hostname function - This function is designed to find and return the hostname of a device using regex

            date = datetime.now().strftime("%Y-%m-%d_%I-%M-%S_%p")

            start_conf_parent_directory = selected_storage_directory + '\\Outputs\\SaveConfOutput\\'  #Create a variable of the absolute path of where the parent directory for output of data capture will be stored
            start_conf_individual_directory = selected_storage_directory + '\\Outputs\\SaveConfOutput\\' + hostname + '\\StartupConf'  #Create a variable of the absolute path of where the running configurations files for devices with the same hostname will be stored

            start_conf_output_file = start_conf_individual_directory + "/" + hostname + '_' + date + '_StartupConfiguration.txt'  #Create a variable for the name and location of the file to be saved. It will be stored as a txt file

            if not os.path.exists(
                    start_conf_parent_directory
            ):  #Check for existence of the parent poll directory - one to store all individual directories - and if not there create it, this is done using the os.path function
                os.makedirs(start_conf_parent_directory)
            else:
                pass

            if not os.path.exists(
                    start_conf_individual_directory
            ):  #Check for existence of the individual poll directory - One to store the current output - and if not there create it, this is done using the os.path function
                os.makedirs(start_conf_individual_directory)
            else:
                pass

            #show and save running conf of routing device

            net_connect.send_command('skip-page-display')

            output_save_conf = net_connect.send_command('show startup-config')

            file = open(
                start_conf_output_file, 'w'
            )  #Set variable file_run to create/Open new/exisitng file and allow it to be written to

            file.write(
                output_save_conf
            )  #Write output from running config from device into the file

            file.close()  #Close file

            #Set the password and username back to empty after completion of configuration
            App.get_running_app().device_username = ''
            App.get_running_app().device_password = ''

            popup = Popup(
                title='',
                content=Label(
                    markup=True,
                    text=
                    "Successfully saved the '[b]Startup Configuration[/b]' of device with hostname '[b]"
                    + hostname + "[/b]'"),
                size_hint=(0.8, 0.3))
            popup.open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()
class BasicConfReload(Screen):

    #Creates the function to execute the reload function

    def BasicConfReloadExecute(self):

        #Try statement to ensure that any errors connecting and configuring the device are handled gracefully and the user is informed of what the potential error was using popups
        try:

            #Try statement to ensure the IP address entered is valid. If it is an invalid address the ipaddress module will raise a value error, at which point the user is informed that a valid IP address is required using a popup
            try:

                device_ip_address = self.ids._Basic_Conf_Reload_Layout_.ids.IPv4AddressTextInput.text
                ipaddress.ip_address(device_ip_address)

            #ipaddress raises a value error when an invalid IP address is used
            except ValueError:

                Factory.InvalidIPAddressPopup().open()
                return  #Exit from the function

            #If statement to ensure user has entered a username or password
            if App.get_running_app(
            ).device_username == '' or App.get_running_app(
            ).device_password == '':

                Factory.NoUserOrPassPopup().open()
                return  #Exit from the function

            else:

                device_username = App.get_running_app().device_username
                device_password = App.get_running_app().device_password

            device = {
                'device_type': 'cisco_ios',
                'ip': device_ip_address,
                'username': device_username,
                'password': device_password,
            }

            net_connect = ConnectHandler(**device)

            output = net_connect.send_command_timing('reload')

            #If statements that check the output for various strings to ensure that the correct commands are sent
            if 'Proceed with reload' in output:  #If this string is detected the tool will send a newline, which will start the reload
                output += net_connect.send_command_timing('\n')

                #Set the password and username back to empty after completion of configuration
                App.get_running_app().device_username = ''
                App.get_running_app().device_password = ''

                #Creates and displays a popup to inform user that reload was successful
                popup = Popup(
                    title='',
                    content=Label(
                        markup=True,
                        text="Succesful reload of device with IP address '[b]"
                        + device_ip_address + "[/b]'"),
                    size_hint=(0.5, 0.3))
                popup.open()

            if 'System configuration has been modified' in output:  #If this string is detected the tool will send the below commands to start the reload
                output += net_connect.send_command_timing('yes')
                output += net_connect.send_command_timing('\n')
                #print(output)

                #Set the password and username back to empty after completion of configuration
                App.get_running_app().device_username = ''
                App.get_running_app().device_password = ''

                #Creates and displays a popup to inform user that reload was successful
                popup = Popup(
                    title='',
                    content=Label(
                        markup=True,
                        text="Succesful reload of device with IP address '[b]"
                        + device_ip_address + "[/b]'"),
                    size_hint=(0.5, 0.3))
                popup.open()

            else:  #If neither set of strings are detected the tool will display a failed configuration popup
                #Creates and displays a popup to inform user that reload has failed
                popup = Popup(
                    title='',
                    content=Label(
                        markup=True,
                        text="Failed to reload device with IP address '[b]" +
                        device_ip_address + "[/b]'"),
                    size_hint=(0.5, 0.3))
                popup.open()

        #Except error to catch when Credentials are incorrect, informs the user of the error using a popup defined in the MainApplication.kv
        except AuthenticationException:

            Factory.NetmikoAuthenticateFailurePopup().open()

        #Except error to catch when Netmiko timeouts and is unable to connect to device, informs the user of the error using a popup defined in the MainApplication.kv
        except NetMikoTimeoutException:

            Factory.NetmikoTimeoutPopup().open()

    #Function to open the credential entry popup

    def OpenCredentialPopup(self):

        self.the_popup = DeviceUsernameAndPasswordPopup()
        self.the_popup.open()