def closeWindow(self): tmpDbPassword = self.databasePasswordField.text() tmpFtpPassword = self.ftpPasswordField.text() if checkDatabaseConnection(tmpDbPassword): if checkFTPConnection(tmpFtpPassword): self.passwordManager.setDatabasePassword( self.databasePasswordField.text()) self.passwordManager.setFtpPassword( self.ftpPasswordField.text()) self.passwordsEntered = True self.dialog.close() else: messageWindow( "FTP: Connection Error", "Error raised when connecting to FTP, please check your details and try again", True) self.databasePasswordField.setText("") self.ftpPasswordField.setText("") else: messageWindow( "Database: Connection Error", "Error raised when connecting to Database server, please check your details and try again", True) self.databasePasswordField.setText("") self.ftpPasswordField.setText("")
def refreshPage(self): try: if self.blocked: secondaryWindows.messageWindow("Process is currently running", "Cannot refresh page when units are being updated", False) else: self.fromConfiguration.addItem("Loading") self.toConfiguration.addItem("Loading") self.osVersions.addItem("Loading") self.clearProgressText() self.fromConfiguration.clear() self.toConfiguration.clear() thread = FileGrabber(self.window) thread.setup(self, ".conf",self.ftpPassword) thread.trigger.connect(self.fillComboBox) thread.start() self.threads.append(thread) thread = FileGrabber(self.window) thread.setup(self, ".tgz",self.ftpPassword) thread.trigger.connect(self.fillComboBox) thread.start() self.threads.append(thread) self.initialDevicePassword.setText("") self.consolePasswordField.setText("") self.willCloneToBackup.setChecked(False) self.toPortNo.setText("") self.fromPortNo.setText("") except Exception as e: print(str(e)) secondaryWindows.messageWindow("Error Refreshing", "An error occured clearing page and connecting to FTP server, try again later", True)
def updateDots(self, code): try: #first clear out layout #self.commitDotLayout self.dots.clear() layout = self.commitDotLayout.layout() self.clearLayout(layout) self.scrollArea.setWidgetResizable(True) if code == 0 and not self.currentFilesHistory == None: # only deal with conf code from deployment class historyList = list(self.currentFilesHistory.keys()) historyList = self.sortTimeStamps(historyList) for index in range(0, len(historyList)): dot = QRadioButton() dot.setText(historyList[index]) dot.setStyleSheet("border: 1px solid rgb(96,125,139);") dot.clicked.connect(self.displayFile) layout.insertWidget(0, dot) self.dots.append(dot) elif code == 1: # only deal with conf code from deployment class secondaryWindows.messageWindow( "Error", "An error occured pulling down info from the database, check your details and try again", True) elif self.currentFilesHistory == None: secondaryWindows.messageWindow( "Error", "An error occured pulling down info from the database, check your details and try again", True) except Exception as e: print(str(e))
def refreshPage(self): self.canUseCombobox = False if self.blocked: secondaryWindows.messageWindow("Process is currently running", "Cannot refresh", False) else: # clear fields self.commitDescription.setText("") self.commitTitle.setText("") self.whatsRemoved.setText("") self.whatsAdded.setText("") self.selectedConfig.clear() self.openFileButton.setText("Select File") self.fileToPush = None self.dots.clear() layout = self.commitDotLayout.layout() self.clearLayout(layout) dot = QRadioButton() dot.setText("Commits Appear Here") dot.setStyleSheet("border: 1px solid rgb(96,125,139);") layout.insertWidget(0, dot) self.dots.append(dot) self.startDatabaseGrabber() self.getFileHistory()
def finishedPushingToRepo(self, code): self.blocked = False if code == 0: self.refreshPage() secondaryWindows.messageWindow( "Success!", "Updated the repository successfully", False) else: secondaryWindows.messageWindow( "Error", "Error pushing the files to the repository", False)
def checkDatabase(self): if self.blocked: secondaryWindows.messageWindow("Process Running", "You're currently runnning an update, wait till this is finished", True) return if not checkDatabaseConnection(self.databasePassword): secondaryWindows.messageWindow("Database: Connection Error", "Error raised when connecting to Database, please check your details and try again", True) return listing = getDatabaseListing(self.databasePassword) if not listing is None: secondaryWindows.displayDatabaseWindow(listing) elif listing is None: secondaryWindows.messageWindow("Database is empty", "There is no data to pull from your database", False) return elif listing is "Error": secondaryWindows.messageWindow("Database Error", "There was an error pulling data from your database", True)
def fillComboBox(self, code): self.canUseCombobox = False if code == 0 and not self.confFiles == None: # only deal with conf code from deployment class nameList = list(self.confFiles.keys()) nameList.sort() for index in range(len(nameList)): self.selectedConfig.addItem(nameList[index]) elif code == 1: # only deal with conf code from deployment class self.selectedConfig.addItem("None Present") elif code == -1: secondaryWindows.messageWindow( "Error", "An error occured pulling down info from the database, check your details and try again", True) elif self.confFiles == None: secondaryWindows.messageWindow( "Error", "An error occured pulling down info from the database, check your details and try again", True) self.canUseCombobox = True
def updateChanges(self, code): if code == 0 and not self.changed is None and not self.removed is None: self.whatsAdded.setText("") self.whatsRemoved.setText("") if len(self.changed) == 0: self.whatsAdded.append("Nothing Has Been Added!") else: for i in range(0, len(self.changed)): self.whatsAdded.append("+ " + self.changed[i]) if len(self.removed) == 0: self.whatsRemoved.append("Nothing Has Been Removed!") else: for i in range(0, len(self.removed)): self.whatsRemoved.append("- " + self.removed[i]) elif code == 1: secondaryWindows.messageWindow("Error", "Error comparing files", True)
def applySettings(self): if self.allSettingsFieldsFilled(): try: #settings are saved in xml file settings_file = open("Settings.xml", "w") #ensure paths are "legal" osPath = self.makeLegalPath(self.ftpOsPath.text()) confPath = self.makeLegalPath(self.ftpConfPath.text()) iniconfPath = self.makeLegalPath(self.ftpIniConfPath.text()) #construct dict to be translated into xml dict = { 'Settings': { 'Ftp-Info': { 'ftpServerAddress': self.ftpServerAddress.text(), 'ftpUsername': self.ftpUsername.text(), 'ftpOsPath': osPath, 'ftpConfPath': confPath, 'ftpIniConfPath': iniconfPath }, 'Console-Info': { 'consoleAddress': self.consoleAddress.text(), 'consoleUsername': self.consoleUsername.text() }, 'Database-Info': { 'databseAddress': self.databseAddress.text(), 'databseUsername': self.databseUsername.text() } } } #convert dict xml = xmltodict.unparse(dict, pretty=True) #write dict to file settings_file.write(xml) settings_file.close() #alert user to success if self.window.notSetup: heading = "Settings Saved! Restarting in 3...." message = "Your Settings are saved, The programme will restart and reload the changes" self.restart = True else: heading = "Settings Saved!" message = "Your Settings are saved" secondaryWindows.messageWindow(heading, message, False) if self.window.notSetup: time.sleep(3) self.window.close() else: self.window.initialiseLauncher() #catch any exceptions when writing to file and cope gracefully except Exception as e: secondaryWindows.messageWindow( "An Error Occured", "There was an error saving your settings the details are as follows: \n " + str(e), True) else: secondaryWindows.messageWindow( "Empty Fields", "Please fill in all fields provided", True)
def openExplorer(self): try: #path returned as tuple though we just want the actual path name, _ = QFileDialog.getOpenFileName( self.window, 'Open File', options=QFileDialog.DontUseNativeDialog) if len(name) < 1: return start = self.findLast(name, "/") + 1 filename = name[start:] extension = filename[len(filename) - 5:] if not ".conf" == extension: secondaryWindows.messageWindow( "Not a configuration file", "Please select a configuration file to push", False) return self.openFileButton.setText(name[start:]) self.fileToPush = name except Exception: secondaryWindows.messageWindow( "Error", "Error parsing file, please ensure " "it's a .conf file with a suitable filename", True)
def displayFile(self): try: if not self.currentFilesHistory is None: timestampText = "" for i in range(len(self.dots)): if self.dots[i].isChecked(): timestampText = self.dots[i].text() if not "Commit" in timestampText: if not self.fileToPush is None: path = self.currentFilesHistory[timestampText] self.fileToPull = path # thread to compare files thread = FileComparer(self.window) thread.setup(self) thread.trigger.connect(self.updateChanges) thread.start() else: secondaryWindows.messageWindow( "Select File", "Please select a file first to compare them", False) except Exception as e: print(str(e))
def startPush(self): if not self.blocked: if self.fileToPush is None: secondaryWindows.messageWindow("No File Selected", "Please select a file to push", False) return elif len(self.commitTitle.text()) < 1: secondaryWindows.messageWindow( "No Commit Title", "Please enter a title for your commit", False) return elif len(self.commitDescription.toPlainText()) < 1: secondaryWindows.messageWindow( "No Commit description", "Please enter a description for your commit", False) return selectedTimeline = self.selectedConfig.currentText() #see if they want to commit to that files timeline reply = QMessageBox.question( self.window, 'Confirmation', "Are you sure you want to push to " + selectedTimeline + "'s timeline?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.blocked = True thread = RepoInterface(self.window) thread.setup(self) thread.trigger.connect(self.finishedPushingToRepo) thread.start() else: return else: secondaryWindows.messageWindow( "Process is currently running", "There is still a file being pushed", False)
def addToRepo(callingWindow): #Set variable values dbAddress = getDatabaseAddress() db = getDatabase() configTable = getDatabaseTable() dbUsername = getDatabaseUsername() dbPassword = callingWindow.databasePassword ftpUsername = getFtpUsername() ftpPassword = callingWindow.ftpPassword configName = callingWindow.selectedConfig.currentText() deviceSerial = "" configFile = callingWindow.fileToPush commitTitle = callingWindow.commitTitle.text() commitDesc = callingWindow.commitDescription.toPlainText() time = datetime.utcnow().strftime('%d-%m-%Y %H:%M:%S.%f')[:-4] newFileName = time.replace(":", "-")[:-3] newFileName = newFileName.replace(" ", "_") + ".conf" # Get current Config details getSerial = ("select serial from " + configTable + " where name=\"" + configName + "\" and isPrimary=\"1\"") try: conn = None conn = pymysql.connect(host=dbAddress, port=3306, user=dbUsername, passwd=dbPassword, db=db) cursor = conn.cursor() cursor.execute(getSerial) deviceSerial = cursor.fetchone()[0] conn.close() except Exception as e: print("Serial couldn't be recovered." + str(e)) if not conn is None: conn.close() #Upload file to the FTP Server ftpAddress = getFtpAddress() path = getConfPath() fileContents = "" try: conn = None ftp = None #chck if conf file if not "conf" in configFile: messageWindow("Error!", "Make sure that you are uploading a .conf file,", True) return False else: # Log into FTP ftp = FTP(ftpAddress) ftp.login(ftpUsername, ftpPassword) ftp.cwd(path) #Upload the file file = open(configFile, "rb") ftp.storlines('STOR ' + newFileName, file) ftp.close() file.close() # Get current config info updateCurentRecord = ("update " + configTable + " set " + "isPrimary=\"0\"" + " where name=\"" + configName + "\" and isPrimary=\"1\"") # Send new config details newFileNamePath = path + newFileName addNewRecord = ( "insert into " + configTable + "(name,serial,user,timestamp,path,title,description,isprimary)" + " values (\"" + configName + "\", \"" + deviceSerial + "\",\"" + dbUsername + "\",\"" + time + "\",\"" + newFileNamePath + "\", \"" + commitTitle + "\", \"" + commitDesc + "\", 1)") # Push the data to the database conn = pymysql.connect(host=dbAddress, port=3306, user=dbUsername, passwd=dbPassword, db=db) cursor = conn.cursor() cursor.execute(updateCurentRecord) cursor.execute(addNewRecord) conn.commit() conn.close() return True except Exception as e: print(str(e)) if not conn is None: conn.close() if not ftp is None: ftp.close() return False
def compareFiles(self): if self.canUseCombobox: self.getContentsOfFile( ) # first get the contents of the selected file if self.fileToPush is None: secondaryWindows.messageWindow( "No local file selected", "Need a local file to compare against", False) return else: try: ''' every line that changes means another has been removed we go through both files and compare them, keeping track of line numbers as we go for readibility ''' changed = [] removed = [] currentFile = self.pulledFilesContents.split("\n") newFile = self.fileToPush lineCount = 1 with open(newFile, 'r', encoding='utf-8') as f: for line in f: line = line.strip("\n") line = line.replace("\\\\", "") if len(currentFile) > 1: for i in range(0, len(currentFile)): if not line == currentFile[i]: changed.append("@" + str(lineCount) + ": " + line) removed.append("@" + str(lineCount) + ": " + currentFile[i]) del currentFile[i] break else: del currentFile[i] break else: changed.append("@" + str(lineCount) + ": " + line) lineCount += 1 if len(currentFile) > 1: for i in range(0, len(currentFile)): removed.append("@" + str(lineCount) + ": " + currentFile[i]) lineCount += 1 # go through and make sure it's not just moved to another line if len(changed) > 1 and len(removed) > 1: i = 0 while i < len(changed): line = changed[i] if len(line) > 1: start = line.index(":") line = line[start + 2:] for j in range(0, len(removed)): if line in removed[j]: del changed[i] del removed[j] i -= 1 break i += 1 self.changed = changed self.removed = removed except Exception as e: print(str(e))
def connect_session(self, portNo, ftpPassword,consolePassword, databasePassword,devicePassword ,OsFile, configFile, willClone, updateGui): try: # Get values from user input ftpAddress = getFtpAddress() osFile = getOsPath() + OsFile confPath = getIniConfPath() + configFile username = getConsoleName() + ":" + str(portNo) hostname = getConsoleAddress() # Connect to the console server ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(hostname, 22, username, consolePassword) term = ssh.invoke_shell() # Log into the device if updateGui: self.trigger.emit(11) waitForTerm(term, 60, "login:"******"set cli screen-length 0") # Get device serial number originalVersion = send_command(term, "show system software") xml = send_command(term, "show chassis hardware | display xml") serialNo = parse_xml_serial(xml) # Push serial number to the database updatedTime = pushSerial(getDatabaseAddress(), getDatabaseUsername(), databasePassword,getDatabase(), configFile, serialNo, confPath) if updateGui: self.trigger.emit(44) # Upgrade JUNOS ftpDetails = getFtpUsername() + ":" + ftpPassword + "@" + ftpAddress upgradeOs = "request system software add ftp://" + ftpDetails + osFile + " no-copy no-validate reboot" send_command(term, upgradeOs) # Wait for the device to reboot if updateGui: self.trigger.emit(55) print("upgrading") waitForTerm(term, 180, "login:"******"finished") waitForLogin(term,devicePassword) if updateGui: self.trigger.emit(66) waitForTerm(term, 2, "root") send_command(term, "set cli screen-length 0") # Snapshot to the backup partition if willClone: send_command(term, "request system snapshot media internal slice alternate") time.sleep(15) waitForTerm(term, 60, "root") # Check the version of JUNOS updatedVersion = send_command(term, "show system software") if updateGui: self.trigger.emit(77) # Start applying a configuration to the device if not updatedVersion == originalVersion: send_command(term, "configure") time.sleep(2) send_command(term, "delete") time.sleep(2) send_command(term, "yes") time.sleep(2) # Get the configuration file from the FTP Server send_command(term, "load set ftp://" + ftpAddress + confPath) time.sleep(2) xml = send_command(term, "show snmp location | display xml") time.sleep(2) # Get device deployment location (rollNo) rollNo = "" try: xml = xml.split("<rpc-reply")[1] xml = "<rpc-reply" + xml xml = xml.split("</rpc-reply>")[0] xml += "</rpc-reply>" xmlDict = xmltodict.parse(xml) rollNo = xmlDict['rpc-reply']['configuration']['snmp']['location'] except: print ("No location data.") time.sleep(5) # Push roll number (deployment location) to the database pushRollNo(getDatabaseAddress(), getDatabaseUsername(), databasePassword,getDatabase(),rollNo, updatedTime) time.sleep(5) #Set device root password send_command(term,"set system root-authentication plain-text-password") send_command(term,devicePassword) send_command(term,devicePassword) #confirm password time.sleep(2) #Commit the current configuration send_command(term, "commit and-quit") waitForTerm(term, 60, "root@") send_command(term, "request system autorecovery state save") time.sleep(30) send_command(term, "request system configuration rescue save") time.sleep(30) # Update the progress bar if updateGui: self.trigger.emit(88) # Reboot the device send_command(term, "request system reboot") time.sleep(2) send_command(term, "yes") #Wait for the device to boot waitForTerm(term, 180, "login:"******"root") xml = send_command(term, "show configuration snmp location | display xml") time.sleep(5) # Get device deployment location (rollNo) - in order to perform a final check checkRollNo="" try: xml = xml.split("<rpc-reply")[1] xml = "<rpc-reply" + xml xml = xml.split("</rpc-reply>")[0] xml += "</rpc-reply>" xmlDict = xmltodict.parse(xml) checkRollNo = xmlDict['rpc-reply']['configuration']['snmp']['location'] except: print("No location data.") if rollNo == checkRollNo: print("Deployment successful.") send_command(term, "request system halt in 0") time.sleep(2) send_command(term, "yes") if updateGui: self.trigger.emit(100) else: print("OS wasn't updated correctly, Not applying config, Shutting down") except paramiko.ssh_exception.BadHostKeyException: secondaryWindows.messageWindow("Host Key Error!", "Server’s host key could not be verified", True) except paramiko.ssh_exception.AuthenticationException: secondaryWindows.messageWindow("Authentication Error!", "Authentication failed, Check your details and try again", True) except paramiko.ssh_exception.SSHException: secondaryWindows.messageWindow("Unknown Error!", "Unknown error connecting or establishing an SSH session", True) except socket.gaierror as e: print(str(e))
def beginDeployment(self): try: # if the window is pressed twice if self.blocked: secondaryWindows.messageWindow("Process running.", "Please wait for deployment to finish.", True) return self.threads.clear() # Make sure fields aren't empty if self.fieldsEmpty(): secondaryWindows.messageWindow("Empty Fields", "Please ensure all the fields are filled", True) return # Make sure both port numbers are actually numbers if (not isNumber(self.toPortNo.text()) or not isNumber(self.fromPortNo.text())): secondaryWindows.messageWindow("Not a Number", "Please ensure ports are numbers", True) return # make sure configurations are the same amount as devices being updated fromConf = self.fromConfiguration.currentIndex() toConf = self.toConfiguration.currentIndex() fromPort = int(self.fromPortNo.text()) toPort = int(self.toPortNo.text()) if not (toConf >= fromConf): secondaryWindows.messageWindow("Config List Error", "Please ensure your starting configuration is before your final configuration", True) return if not ((toConf - fromConf) + 1 == (toPort - fromPort) + 1): secondaryWindows.messageWindow("Not Enough Configs", "Please ensure there are enough config files for each specified device", True) return devicePassword = self.initialDevicePassword.text() if not self.checkValidity(devicePassword): secondaryWindows.messageWindow("Password error", """ Please ensure the password is at least 6 characters and contains either a number or an uppercase letter """, True) return # apply patch patch_crypto_be_discovery() configurationsToUse = [] for index in range(fromConf, toConf + 1): configurationsToUse.append(self.confFiles[index]) willBackup = self.willCloneToBackup.isChecked() OsFile = self.osVersions.currentText() ftpPassword = self.ftpPassword consolePassword = self.consolePasswordField.text() databasePassword = self.databasePassword self.progressBar.setValue(0) # check if connections are accessible if not checkFTPConnection(ftpPassword): secondaryWindows.messageWindow("FTP: Connection Error", "Error raised when connecting to FTP server, please check your details and try again", True) return if not checkConsoleConnection(getConsoleAddress(), getConsoleName(), consolePassword): secondaryWindows.messageWindow("Console Server: Connection Error", "Error raised when connecting to Console server, please check your details and try again", True) return if not checkDatabaseConnection(databasePassword): secondaryWindows.messageWindow("Database: Connection Error", "Error raised when connecting to Database, please check your details and try again", True) return self.blocked = True confIndex = 0 # check to see how many devices we're updating if toPort - fromPort + 1 == 1: thread = Updater(self.window) thread.trigger.connect(self.updateProgress) thread.setup(toPort, ftpPassword, consolePassword, databasePassword, devicePassword,OsFile, configurationsToUse[confIndex], willBackup, True) thread.start() self.threads.append(thread) else: for index in range(fromPort, toPort): thread = Updater(self.window) thread.trigger.connect(self.updateProgress) thread.setup(index, ftpPassword, consolePassword, databasePassword, devicePassword, OsFile, configurationsToUse[confIndex], willBackup, False) thread.start() self.threads.append(thread) confIndex += 1 time.sleep(5) # staggered threads to avoid collision # make final thread the one to update GUI thread = Updater(self.window) thread.trigger.connect(self.updateProgress) thread.setup(toPort, ftpPassword, consolePassword, databasePassword, devicePassword, OsFile, configurationsToUse[confIndex], willBackup, True) thread.start() self.threads.append(thread) except Exception as e: print(str(e))