From 1873e21c712089968dc4cf30eaf8ea07c9bc8af3 Mon Sep 17 00:00:00 2001 From: kprkpr Date: Tue, 11 Jun 2019 11:14:38 +0200 Subject: [PATCH] GUI version to control all --- .gitignore | 6 +- BD/emptydatabase.db | Bin 73728 -> 69632 bytes README.MD | 25 +-- api.py | 193 ++++++++++++++-------- client/PCM.fbp | 310 ----------------------------------- client/client.ps1 | 18 +- client/configexample.ps1 | 2 - client/configpcm_example.ini | 2 + client/control.ps1 | 9 +- client/control.pyw | 199 ++++++++++++++++++++++ sysopt.py | 2 +- 11 files changed, 366 insertions(+), 400 deletions(-) delete mode 100644 client/PCM.fbp delete mode 100644 client/configexample.ps1 create mode 100644 client/configpcm_example.ini create mode 100644 client/control.pyw diff --git a/.gitignore b/.gitignore index 90bcb97..451a286 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ **/thumbs.bd *.bak -api.log -client/config.ps1 +*.log +*.out +client/configpcm.ini +client/controltkinter.py BD/database* reports/ __pycache__/ diff --git a/BD/emptydatabase.db b/BD/emptydatabase.db index 99fc3371111899cde18c059b85cbab97066ae806..bc4dcde145a466a7bf816993ea33eaf2d606e31f 100644 GIT binary patch delta 398 zcmZoTz|ydQWrDO|4g&*&AQUqK>3tJ*jCFGu^bD+c`F}9*bILIA`}3dX`^)=|?*Pv? z-rYQUJiEDBxW95%ab4t^$0@_HV6&hAA4fe%F&N#_*Jo^0E=f$vNi8nSNs2E?%`J!r zbK^}g1O%LeTpdGP6+#@Hd|VZjFyxgKG`KjG{0mAl^YfHALtG<56#V>w7-&@y$R2)} zJt;toVuabnrKMql{sAGL{(iww3e6&@C@$keeSCC)_J$?ql%_(g<A;-wO zc_s%FBaL7IY#2XQ%jFP%k1Wx&j9s12mmI1OQ*+N{I#K$(eu&t^e^wfwpo8a&M5 r9K@N-v-yR+M8hNnZdM+k`P`cYKYZX{l;AKifSr>GC?E*L0$@P^(00~C diff --git a/README.MD b/README.MD index 2baa61d..44b2990 100644 --- a/README.MD +++ b/README.MD @@ -2,18 +2,23 @@ Powerful Computer Manager is the super-simple-but-powerful program to automatizate tasks in groups of computers (In a Active Directory site, or not) in a unattended way. It inteed to be a simple SSMS alternative, that inventarizes computers and install/remove software in it, tweak system of run arbitrary commands. -You only have to write simple _cooks_ that are small yaml text files, create groups of computers and assign _cooks_ to this computers, and will be run in the destination computers +You only have to write simple _cooks_ that are small yaml text files, create groups of computers and assign _cooks_ to this groups, and will be run in the destination computers # Disclaimer -I'm using this program in the company I work with 10 computers with Windows 8.1/10 with various software (MSI,EXE,...) and tweaking services, and it works right +I'm using this program in the company I work with 12 computers with Windows 8.1/10 with various software (MSI,EXE,...) and tweaking services, and it works right It's not super-stable and error proof, then, I don't have any responsability if something crashes, but you can open a Issue and will see it # Roadmap -GUI administration in Python to superseed Powershell CLI control program and be cross-platform +- English (Its all in Spanish now). Create a way to translate it +- Security (API 1) +- Stabilize all +- If anyone wants to create a better gui in Python it will be awesome. I used easygui to create a fast usable gui but maybe is not very proffesional.. Anyone? + I had some in TKInter but easygui was very useful without making difficult release it as first usable version + # Requirements @@ -22,16 +27,16 @@ For server you can use Linux or Windows, and in theory any environment that can - Python3 with: - Falcon - Waitress-serve - - Json (Python3 have it preinstalled) - Yaml Python3 deps can be installed it using pip install command (pip install waitress falcon pyyaml) -- For controlling the program, you have to use "control.ps1" using Powershell. Powershell comes with Windows, and can be installed (PWCore) in Linux and macOS (I didn't tested it). +- For controlling the program, you have to start control.pyw using Python3. It doesn't need to be started in server, but it has to be in that folder (Or with configpcm.ini file). + It uses easygui of python3 (pip install easygui) ## Clients - Windows: - Windows 7 SP1 absolute minimal, but its only officially tested in 8.1 and 10, Windows 7 is being old now. - - It needs Powershell 5.0+, lower versions doesn't have some commands, not tested. In Windows 7 and 8.1, you have to update Powershell. (With Powershell 3.0/4.0, some commands like Get-Package doesn't work) - You can update from here: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell?view=powershell-6#upgrading-existing-windows-powershell + - It needs Powershell 5.0+ (Not PWCore versions), lower versions doesnt guaranteed to work, not tested. In Windows 7 and 8.1, you have to update Powershell. (With Powershell 3.0/4.0, some commands like Get-Package doesn't work) + You can update from here: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell?view=powershell-6#upgrading-existing-windows-powershell (Download link: https://www.microsoft.com/en-us/download/details.aspx?id=54616) - Linux: - Not supported yet, but when Windows be stable, I will try to do it in some way @@ -42,10 +47,10 @@ For server you can use Linux or Windows, and in theory any environment that can - Install dependencies - Open sysopt.py file and change values if needed - Use bat file if you are in Windows to run the python server, or sh file if you are in Linux environment -- Go to Client folder and copy "configexample.ps1" to "config.ps1" and change their values to yours. -- For clients to work, they have to run "client\client.ps1" every 30 mins/1 hour or so, I did using Active Directory and a Task Scheduled GPO (Copying client.ps1 and config.ps1 files from Client to SysVol\DomainName\Scripts\) +- Go to Client folder and copy "configpcm_example.ini" to "configpcm.ini" and change their values to yours. +- For clients to work, they have to run "client\client.ps1" every 30 mins/1 hour or so, I did using Active Directory and a Task Scheduled GPO (Copying client.ps1 and configpcm.ini files from Client to SysVol\DomainName\Scripts\) - User: SYSTEM (Or NT\SYSTEM) - Command: powershell.exe - Args: -executionpolicy bypass -windowstyle hidden -noninteractive -nologo -file "\\SERVER\SysVol\DOMAINNAME\scripts\client.ps1" - Note: client.ps1 has to have control.ps1 file with it + Note: client.ps1 has to have controlpcm.ini file with it - If you don't have Active Directory, you can do the Task Scheduled task in every computer without problems, and using another Shared folder, it will work \ No newline at end of file diff --git a/api.py b/api.py index 39d5502..73882dc 100644 --- a/api.py +++ b/api.py @@ -1,5 +1,6 @@ #!/usr/bin/python3 -# Build 5 +BUILD = 12 +API_VER = 0 #Its beta, API 1 will have password checks enabled ##It can be run directly with "waitress-serve --port=3333 api:api" import falcon import random @@ -31,7 +32,7 @@ class getComputerExists(object): UUID = value if ComputerName is None: - response.media = {'RESULT': '0'} + response.media = {'RESULT': 'ERROR'} else: result = sql.select("SELECT COUNT(*) 'RESULT' FROM COMPUTERS WHERE Name='"+ComputerName+"' AND UUID='"+UUID+"'") @@ -40,11 +41,14 @@ class getComputerExists(object): sql.insert("UPDATE COMPUTERS SET UUID='"+UUID+"' WHERE Name='"+ComputerName+"'") response.media = {'RESULT': '1'} return + elif sql.select("SELECT COUNT(*) 'RESULT' FROM COMPUTERS WHERE Name='"+ComputerName+"'")[0]['RESULT'] != 0: + response.media = {'RESULT': 'ERROR'} + return if sysopt.addComputers == True: sql.insert("INSERT INTO COMPUTERS (`Name`,`UUID`) VALUES('"+ComputerName+"','"+UUID+"')") response.media = {'RESULT': '1'} else: - response.media = {'RESULT': '0'} + response.media = {'RESULT': 'ERROR'} else: #Exists response.media = {'RESULT': '1'} @@ -93,8 +97,28 @@ class getComputersGrp(object): #List of computers in a group GroupID = value data = sql.select("SELECT * FROM COMPUTERS WHERE ID_C IN (SELECT ID_C FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"') ORDER BY Name") response.media = data - -class getCook(object): # Get the list of cooks for a computer to implement (Var all=1 for see all of, implemented or not) + +## +# getCookAll: Get the list of cooks added at least to one group +# /get/cookall? +# NoParams +## +class getCookAll(object): + def on_get(self,request, response): + logit(request) + data = sql.select("SELECT DISTINCT CookName FROM COOKS_IDG ORDER BY CookName ASC") #All cooks + response.media = data + + +## +# getCookPend: Get the list of cooks that are pending to deploy to a computer +# /get/cookpend? +# @param ComputerName/ComputerID -> Computer to deploy it +# @param GroupName/GroupID: Groups +# @param SeeAll: Send all cooks from this computer +# @param UUID: Check UUID validation of this computer (Not implemented yet) +## +class getCookPend(object): # Get the list of cooks for a computer to implement (Var all=1 for see all of, implemented or not) def on_get(self,request, response): logit(request) ComputerID,GroupID, SeeAll =None, None, None @@ -110,10 +134,10 @@ class getCook(object): # Get the list of cooks for a computer to implement (Var if key == "SeeAll": SeeAll = str(value) - if ComputerID is None and GroupID is None: - response.media = {'ERROR': 'I need a Group or Computer to search'} + if ComputerID is None and GroupID is None: # Error of null parameters + response.media = {'TEXT': 'I need a Group or Computer to search','RESULT':'ERROR'} elif ComputerID is not None and SeeAll is None: - data = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer + data = sql.select("SELECT DISTINCT CookName FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer fordelete = [] for i in range(len(data)): for key, value in data[i].items(): #Iterate in a dict (json) @@ -127,11 +151,16 @@ class getCook(object): # Get the list of cooks for a computer to implement (Var for x in reversed(fordelete): #Deleting cooks that are implemented in client. Reverse order (Because is an array and index..) data.pop(x) response.media = data - else: - response.media = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer + else: # SeeAll. Send all cooks + response.media = sql.select("SELECT DISTINCT CookName FROM COOKS_IDG WHERE ID_G IN (SELECT ID_G FROM COMPUTER_GROUP WHERE ID_C = '"+ComputerID+"')") #All cooks for this computer - -class setCook(object): #Assign Cook to group +## +# addCookGrp: Assign Cook to group +# /add/cookgrp? +# @param GroupName/GroupID -> Group to show cooks +# @param CookName -> Cook to assign +## +class addCookGrp(object): #Assign Cook to group def on_get(self, request, response): logit(request) GroupID, CookName = None, None # Initialize @@ -144,12 +173,12 @@ class setCook(object): #Assign Cook to group if key == "GroupID": GroupID = value if GroupID is None or exists is False: - response.media = {'ERROR': 'GroupID is not defined or Cook not exists'} + response.media = {'TEXT': 'GroupID is not defined or Cook not exists','RESULT':'ERROR'} elif int(sql.select("SELECT COUNT(*) 'COUNT' FROM COOKS_IDG WHERE `ID_G`='"+GroupID+"' AND `CookName`='"+CookName+"'")[0]['COUNT']) > 0: - response.media = {'WARNING': 'This union GROUP-CookName exists'} + response.media = {'TEXT': 'This union GROUP-CookName exists','RESULT':'0'} else: result = sql.insert("INSERT INTO COOKS_IDG (`ID_G`,`CookName`) VALUES ('"+GroupID+"','"+CookName+"')") - response.media = result + response.media = {'TEXT': 'OK'} class delCookGrp(object): #Delete cook from a group def on_get(self,request,response): @@ -167,9 +196,9 @@ class delCookGrp(object): #Delete cook from a group if GroupID is not None and CookName is not None: result = sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"' AND CookName='"+CookName+"'") - response.media = result + #response.media = result else: - response.media = {'RESULT': 'Error, no Group, or CookName does\'t exists'} + response.media = {'TEXT': 'Error, no Group, or CookName does\'t exists','RESULT':'ERROR'} class delEmptyPcsGroup(object): #Delete all computers from a group def on_get(self,request,response): @@ -185,11 +214,15 @@ class delEmptyPcsGroup(object): #Delete all computers from a group result = sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"'") response.media = result else: - response.media = {'RESULT': 'Error, this group doesn\'t exists'} + response.media = {'TEXT': 'Error, this group doesn\'t exists','RESULT':'ERROR'} - -class getCookGrp(object): # Get cooks from a Group +## +# getCookGrp: Get cooks from a Group +# /get/cookgrp? +# @param GroupName/GroupID -> Group to show cooks +## +class getCookGrp(object): def on_get(self,request,response): logit(request) GroupID = None # Initialize @@ -203,8 +236,13 @@ class getCookGrp(object): # Get cooks from a Group result = sql.select("SELECT * FROM COOKS_IDG WHERE ID_G='"+GroupID+"'") response.media = result else: - response.media = {'RESULT': 'Error, no Group selected'} + response.media = {'TEXT': 'Error, no Group selected','RESULT':'ERROR'} +## +# getGrpCook: Get groups from a Cook +# /get/grpcook? +# @param CookName -> Cook to show groups +## class getGrpCook(object): # Get Groups of a Cook def on_get(self,request,response): logit(request) @@ -218,7 +256,7 @@ class getGrpCook(object): # Get Groups of a Cook if CookName is not None: response.media = sql.select("SELECT Name FROM GROUPS WHERE ID_G IN (SELECT ID_G FROM COOKS_IDG WHERE CookName='"+CookName+"')") else: - response.media = {'RESULT': 'Error, no Cook selected'} + response.media = {'TEXT': 'Error, no Cook selected','RESULT':'ERROR'} class getStatusCook(object): # Get Status of a Cook (If Brief=1 is sent too, brief status (Completed:X, updated:X..)) def on_get(self,request,response): @@ -233,7 +271,7 @@ class getStatusCook(object): # Get Status of a Cook (If Brief=1 is sent too, bri if CookName is not None: response.media = sql.select("SELECT Name, Revision, Error FROM COMPUTERS,COOKS_STATUS WHERE CookName = '"+CookName+"' AND COMPUTERS.ID_C=COOKS_STATUS.ID_C") else: - response.media = {'RESULT': 'Error, no Cook selected'} + response.media = {'TEXT': 'Error, no Cook selected','RESULT':'ERROR'} class getLastRevisionCook(object): # Get Number Revision (Revision=X) def on_get(self,request,response): @@ -250,8 +288,13 @@ class getLastRevisionCook(object): # Get Number Revision (Revision=X) filecook = myfile.read() response.media = {'Revision': str(yaml.safe_load(filecook)['revision'])} else: - response.media = {'RESULT': 'Error, no Cook selected'} + response.media = {'TEXT': 'Error, no Cook selected','RESULT':'ERROR'} +## +# addComputer: Add a computer in database +# /add/computer? +# @param ComputerName -> ComputerName to add +## class addComputer(object): def on_get(self, request, response): logit(request) @@ -261,7 +304,7 @@ class addComputer(object): ComputerName = value if ComputerName is None: - response.media = {'RESULT': 'Error, you need a ComputerName to add'} + response.media = {'TEXT': 'Error, you need a ComputerName to add','RESULT':'ERROR'} else: result = sql.insert("INSERT INTO COMPUTERS (Name) VALUES ('"+ComputerName+"')") response.media = result @@ -276,7 +319,7 @@ class addGroup(object): if GroupName is None: - response.media = {'RESULT': 'Error, you need a GroupName to add'} + response.media = {'TEXT': 'Error, you need a GroupName to add','RESULT':'ERROR'} else: result = sql.insert("INSERT INTO GROUPS (Name) VALUES ('"+GroupName+"')") response.media = result @@ -292,7 +335,7 @@ class delGroup(object): #Delete group GroupID = str(sql.select("SELECT ID_G FROM GROUPS WHERE Name='"+value+"'")[0]['ID_G']) if GroupID is None: - response.media = {'RESULT': 'Error, you need a GroupName to delete'} + response.media = {'TEXT': 'Error, you need a GroupName to delete','RESULT':'ERROR'} else: sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_G='"+GroupID+"'") sql.insert("DELETE FROM COOKS_IDG WHERE ID_G='"+GroupID+"'") @@ -313,10 +356,10 @@ class updGroup(object): #Delete group if Count == "0": GroupNewName = value else: - response.media = {'RESULT': 'Error, New group name exists'} + response.media = {'TEXT': 'Error, New group name exists','RESULT':'ERROR'} if GroupID is None or GroupNewName is None: - response.media = {'RESULT': 'Error, you need a GroupName and new name to update name'} + response.media = {'TEXT': 'Error, you need a GroupName and new name to update name','RESULT':'ERROR'} else: result = sql.insert("UPDATE GROUPS SET Name='"+GroupNewName+"' WHERE ID_G='"+GroupID+"'") response.media = result @@ -336,7 +379,7 @@ class addGrpComputer(object): #Add computer to a group (Local) GroupID = value if ComputerID is None or GroupID is None: - response.media = {'RESULT': 'Error, you need a Name and Group to add'} + response.media = {'TEXT': 'Error, you need a Name and Group to add','RESULT':'ERROR'} else: result = sql.insert("INSERT INTO COMPUTER_GROUP (ID_C,ID_G) VALUES ('"+ComputerID+"','"+GroupID+"')") response.media = result @@ -356,7 +399,7 @@ class delGrpComputer(object): #Del computer from a group GroupID = value if ComputerID is None or GroupID is None: - response.media = {'RESULT': 'Error, you need a Name and Group to add'} + response.media = {'TEXT': 'Error, you need a Name and Group to add','RESULT':'ERROR'} else: result = sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_C='"+ComputerID+"' AND ID_G='"+GroupID+"'") response.media = result @@ -370,10 +413,12 @@ class delComputer(object): #Delete computer if key == "ComputerName": ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C']) if key == "ComputerID": - ComputerID = value + ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE ID_C='"+value+"'")[0]['ID_C']) if ComputerID is None: - response.media = {'RESULT': 'Error, you need a Name/ComputerID to update data'} + response.media = {'TEXT': 'Error, you need a Name/ComputerID to update data','RESULT':'ERROR'} + elif ComputerID=='': + response.media = {'TEXT': 'Error, this Name/ID is not valid','RESULT':'ERROR'} else: sql.insert("DELETE FROM COMPUTER_GROUP WHERE ID_C='"+ComputerID+"'") sql.insert("DELETE FROM COOKS_STATUS WHERE ID_C='"+ComputerID+"'") @@ -390,18 +435,18 @@ class updComputer(object): try: ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C']) except: - response.media = {'RESULT': 'Error: Computer not exists in database'} + response.media = {'TEXT': 'Error: Computer not exists in database','RESULT':'ERROR'} break if key == "ComputerID": ComputerID = value if key == "UUID": UUID = value if ComputerID is None and response.media is None: - response.media = {'RESULT': 'Error, you need a ComputerName/ComputerID to update data'} + response.media = {'TEXT': 'Error, you need a ComputerName/ComputerID to update data','RESULT':'ERROR'} elif response.media is None and ComputerID is not None and UUID is not None: Count = str(sql.select("SELECT COUNT(*) 'Count' FROM COMPUTERS WHERE UUID='"+UUID+"' AND ID_C='"+ComputerID+"'")[0]['Count']) if Count == "0": - response.media = {'RESULT': 'Error, computer doesn\'t exists in database'} + response.media = {'TEXT': 'Error, computer doesn\'t exists in database','RESULT':'ERROR'} else: for key, value in request.params.items(): if key == "SOVersion": @@ -419,7 +464,12 @@ class updComputer(object): if key == "CPUName": response.media = sql.insert("UPDATE COMPUTERS SET CPUName='"+value+"' WHERE ID_C='"+ComputerID+"'") - +## +# updCookName: Rename cook name +# /upd/cookname? +# @param CookName -> Original Cook Name +# @param CookNewName -> New Cook Name +## class updCookName(object): def on_get(self, request, response): logit(request) @@ -429,16 +479,16 @@ class updCookName(object): if os.path.isfile('cooks/'+value+'.yaml'): CookName= value else: - response.media = {'RESULT': 'Error: Cook not exists in folder'} + response.media = {'TEXT': 'Error: Cook not exists in folder','RESULT':'ERROR'} break if key == "CookNewName": if os.path.isfile('cooks/'+value+'.yaml'): - response.media = {'RESULT': 'Error: There is a cook with the new name in folder!'} + response.media = {'TEXT': 'Error: There is a cook with the new name in folder!','RESULT':'ERROR'} break else: CookNewName= value if CookName is None or CookNewName is None: - response.media = {'RESULT': 'Error, you need the old and new Cook Name to update data'} + response.media = {'TEXT': 'Error, you need the old and new Cook Name to update data','RESULT':'ERROR'} elif response.media is None: old_file = os.path.join("cooks", CookName+'.yaml') new_file = os.path.join("cooks", CookNewName+'.yaml') @@ -456,7 +506,7 @@ class loadCook(object): if os.path.isfile('cooks/'+value+'.yaml'): CookName= value else: - response.media = {'RESULT': 'Error: Cook not exists in folder'} + response.media = {'TEXT': 'Error: Cook not exists in folder','RESULT':'ERROR'} break if key == "ComputerName": ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C']) @@ -466,11 +516,11 @@ class loadCook(object): UUID = value if CookName is None and response.media is None: - response.media = {'RESULT': 'Error, you need a CookName to load it'} + response.media = {'TEXT': 'Error, you need a CookName to load it','RESULT':'ERROR'} elif response.media is None: Count = str(sql.select("SELECT COUNT(*) 'Count' FROM COMPUTERS WHERE UUID='"+UUID+"' AND ID_C='"+ComputerID+"'")[0]['Count']) if Count == "0": - response.media = {'RESULT': 'Error, computer doesn\'t exists in database'} + response.media = {'TEXT': 'Error, computer doesn\'t exists in database','RESULT':'ERROR'} else: with open('cooks/'+CookName+'.yaml', 'r') as myfile: data = myfile.read() @@ -485,7 +535,7 @@ class setCookStatus(object): if os.path.isfile('cooks/'+value+'.yaml'): # You have to know that cook exists. CookName= value else: - response.media = {'RESULT': 'Error: Cook not exists in folder'} + response.media = {'TEXT': 'Error: Cook not exists in folder','RESULT':'ERROR'} break if key == "ComputerName": ComputerID = str(sql.select("SELECT ID_C FROM COMPUTERS WHERE Name='"+value+"'")[0]['ID_C']) @@ -499,7 +549,7 @@ class setCookStatus(object): ErrorDesc = value if CookName is None and response.media is None: - response.media = {'RESULT': 'Error, you need a CookName to load it'} + response.media = {'TEXT': 'Error, you need a CookName to load it','RESULT':'ERROR'} elif response.media is None and CookName is not None and ComputerID is not None and Revision is not None and Error is not None: statt = sql.select("SELECT COUNT(*) 'RESULT' FROM COOKS_STATUS WHERE CookName='"+CookName+"' AND ID_C='"+ComputerID+"'")[0]['RESULT'] if statt == 0: @@ -509,33 +559,48 @@ class setCookStatus(object): #UPDATE, NOT NEW response.media = sql.insert("UPDATE COOKS_STATUS SET Revision='"+Revision+"',`Error`='"+Error+"',`ErrorDesc`='"+ErrorDesc+"' WHERE ID_C='"+ComputerID+"' AND CookName='"+CookName+"'") else: - response.media = {'RESULT': 'Error in parameters...'} + response.media = {'TEXT': 'Error in parameters...','RESULT':'ERROR'} logit(response.media) api = falcon.API() -api.add_route('/get/computers', getComputers()) #Get list of computer -api.add_route('/get/computerexists', getComputerExists()) #Returns 0 or 1 (name status) -api.add_route('/get/groups', getGroups()) #Get groups of a computer (Or list if not args) -api.add_route('/get/computersgrp', getComputersGrp()) #Get computers in a group -api.add_route('/get/cook', getCook()) #Get cooks from a group -api.add_route('/set/cook', setCook()) #Assign cook to group +api.add_route('/add/computer', addComputer()) #Add computer +api.add_route('/add/group', addGroup()) #Add group to the list of local groups +api.add_route('/add/cookgrp', addCookGrp()) #Assign cook to group +api.add_route('/add/grpcomputer', addGrpComputer()) #Add computer to a group +api.add_route('/del/computer', delComputer()) #Delete computer api.add_route('/del/cookgrp', delCookGrp()) # Deassign cook from a group api.add_route('/del/emptypcsgroup', delEmptyPcsGroup()) # Delete all computers from a group -api.add_route('/get/cookgrp', getCookGrp()) # See cooks that have a determinated group -api.add_route('/get/grpcook', getGrpCook()) # See groups of a determinated cook -api.add_route('/get/statuscook', getStatusCook()) # See status of a cook -api.add_route('/get/lastrevisioncook', getLastRevisionCook()) # Returns number of last revision of a cook -api.add_route('/add/computer', addComputer()) #Add computer -api.add_route('/del/computer', delComputer()) #Delete computer -api.add_route('/add/group', addGroup()) #Add group to the list of local groups api.add_route('/del/group', delGroup()) #Delete group -api.add_route('/upd/group', updGroup()) #Update group name -api.add_route('/add/grpcomputer', addGrpComputer()) #Add computer to a group api.add_route('/del/grpcomputer', delGrpComputer()) #Delete computer from a group - +api.add_route('/get/computerexists', getComputerExists()) #Returns 0 or 1 (name status) +api.add_route('/get/computers', getComputers()) #Get list of computer +api.add_route('/get/computersgrp', getComputersGrp()) #Get computers in a group +api.add_route('/get/cookall', getCookAll()) #Get all cooks implemented in one group at least +api.add_route('/get/cookgrp', getCookGrp()) # See cooks that have a determinated group +api.add_route('/get/cookpend', getCookPend()) #Get cooks pending to implement to a computer +api.add_route('/get/groups', getGroups()) #Get groups of a computer (Or list if not args) +api.add_route('/get/grpcook', getGrpCook()) # See groups of a determinated cook +api.add_route('/get/lastrevisioncook', getLastRevisionCook()) # Returns number of last revision of a cook +api.add_route('/get/statuscook', getStatusCook()) # See status of a cook +api.add_route('/load/cook', loadCook()) # Load a cook (Transfer in json way) +api.add_route('/set/cookstatus', setCookStatus()) # Update status of a cook in a computer (OLD) +api.add_route('/upd/cookstatus', setCookStatus()) # Update status of a cook in a computer api.add_route('/upd/computer', updComputer()) #Update data of computer api.add_route('/upd/cookname', updCookName()) #Update file name of cook and SQL references to it. -api.add_route('/load/cook', loadCook()) # Load a cook (Transfer in json way) -api.add_route('/set/cookstatus', setCookStatus()) # Update status of a cook in a computer +api.add_route('/upd/group', updGroup()) #Update group name + + +#api.add_route('/check/password', checkPassword()) #Check admin password +#api.add_route('/upd/password', updPassword()) # Update password (Or create it not exists) +#api.add_route('/get/password', getPassword()) # Get password (Will be '' if password not exists) + +class getApiVer(object): + def on_get(self, request, response): + logit(request) + response.media = {'API':API_VER} +api.add_route('/get/apiver', getApiVer()) # Get API version + +print("Build: "+str(BUILD)) +print("API Version: "+str(API_VER)) diff --git a/client/PCM.fbp b/client/PCM.fbp deleted file mode 100644 index 52ac445..0000000 --- a/client/PCM.fbp +++ /dev/null @@ -1,310 +0,0 @@ - - - - - ; - C++ - 1 - source_name - 0 - 0 - res - UTF-8 - connect - - 1000 - none - - 0 - MyProject1 - - . - - 1 - 1 - 1 - 1 - UI - 0 - 0 - - 0 - wxAUI_MGR_DEFAULT - - wxBOTH - - 1 - 1 - impl_virtual - - - - 0 - wxID_ANY - - - MainFrame - - 749,396 - wxDEFAULT_FRAME_STYLE - ; ; forward_declare - - - - - wxTAB_TRAVERSAL - 1 - - - - 1 - 1 - - - 0 - wxID_ANY - MyMenuBar - - - m_menubar1 - protected - - - - ; ; forward_declare - - - - - - Equipos - m_computers - protected - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Añadir equipo - scomp_addc - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Borrar equipo - scomp_deletec - none - - - - - - Grupos - m_groups - protected - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Añadir Grupo - sgroup_addg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Borrar grupo - sgroup_delg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Ver equipos de un grupo - sgroup_seeg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Vaciar grupo - sgroup_emptyg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Cambiar nombre de un grupo - sgroup_reng - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Recetas asignadas a un grupo - sgroup_cooksg - none - - - - - - Recetas - m_cooks - protected - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Recetas de un grupo - scook_cooksg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Añadir receta a un grupo - scook_addcookg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Borrar receta de un grupo - scook_deletecookg - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Renombrar receta - scook_renamecook - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Detalles de implantación de una receta - scook_detailscook - none - - - - - - Reportes - m_reports - protected - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Reporte de equipos - srep_rcomp - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Reporte de grupos - srep_rgroup - none - - - - - - 0 - 1 - - wxID_ANY - wxITEM_NORMAL - Reporte de recetas - srep_rcook - none - - - - - - - - diff --git a/client/client.ps1 b/client/client.ps1 index 6f3ff34..01cf59e 100644 --- a/client/client.ps1 +++ b/client/client.ps1 @@ -1,12 +1,14 @@ -# Build 2 +# Build 5 $root = $PSCommandPath | Split-Path -Parent -. $root\config.ps1 +$server = (Get-Content "$root\configpcm.ini" | select -Skip 1 | ConvertFrom-StringData).server +$resources = (Get-Content "$root\configpcm.ini" | select -Skip 1 | ConvertFrom-StringData).resources -$computerName = $env:COMPUTERNAME + +$computerName = [System.Net.Dns]::GetHostName() $UUID=(get-wmiobject Win32_ComputerSystemProduct).UUID $exists = Invoke-RestMethod -Method Get -Uri "$server/get/computerexists?ComputerName=$computerName&UUID=$UUID" -if ($exists.Result -eq 0){ +if ($exists.Result -eq "ERROR"){ Write-Host "Computer outside database:" $computerName exit } @@ -30,7 +32,7 @@ $lastUser = (Get-CimInstance -ClassName Win32_ComputerSystem -Property UserName Invoke-RestMethod -Method Get -Uri "$server/upd/computer?ComputerName=$computerName&UUID=$UUID&LastUser=$lastUser" | out-null -$cooks = Invoke-RestMethod -Method Get -Uri "$server/get/cook?ComputerName=$computerName&UUID=$UUID" +$cooks = Invoke-RestMethod -Method Get -Uri "$server/get/cookpend?ComputerName=$computerName&UUID=$UUID" foreach ($CookName in $cooks){ Set-Location $env:temp # For downloading/copying items, use system temp location @@ -79,7 +81,7 @@ foreach ($CookName in $cooks){ $err = 1 } } - "KILL_PROCESS" { + {$_ -in "KILL_PROCESS","KILLPROCESS"} { $p = Get-Process -Name "$param" if ($? -eq $true){ # Only do something if exists Stop-Process -InputObject $p -Force -ErrorVariable errvar -ErrorAction Continue @@ -90,7 +92,7 @@ foreach ($CookName in $cooks){ } "CMD" { # Run a cmd command. Note: Runs at high priv. cmd.exe /c "$param" - if ($? -eq $false){ # Error in CMD + if ($LASTEXITCODE -ne 0){ # Error in CMD $err = 1 } } @@ -208,7 +210,7 @@ foreach ($CookName in $cooks){ if ($cook.runever -eq "1"){ $err = -1 # This is for run but not for error, is by cook saw. } - Invoke-RestMethod -Method Get -Uri "$server/set/cookstatus?ComputerName=$computerName&UUID=$UUID&CookName=$CookName&Revision=$CookRevision&Error=$err&ErrorDesc=$errvar" | out-null + Invoke-RestMethod -Method Get -Uri "$server/upd/cookstatus?ComputerName=$computerName&UUID=$UUID&CookName=$CookName&Revision=$CookRevision&Error=$err&ErrorDesc=$errvar" | out-null #Delete files copied to temp for saving space foreach ($element in $filesCopied) { diff --git a/client/configexample.ps1 b/client/configexample.ps1 deleted file mode 100644 index ee03950..0000000 --- a/client/configexample.ps1 +++ /dev/null @@ -1,2 +0,0 @@ -$server = "http://miserver.dominio:3333" #Server that runs api service -$resources = "\\MISERVER\REPOFOLDER" #Where files for copy are (Not cooks) \ No newline at end of file diff --git a/client/configpcm_example.ini b/client/configpcm_example.ini new file mode 100644 index 0000000..4c13804 --- /dev/null +++ b/client/configpcm_example.ini @@ -0,0 +1,2 @@ +server=http://miserver.dominio:3333 +resources=\\\\MISERVER\\REPOFOLDER \ No newline at end of file diff --git a/client/control.ps1 b/client/control.ps1 index 5d69a84..4bd49d8 100644 --- a/client/control.ps1 +++ b/client/control.ps1 @@ -1,6 +1,9 @@ -# Build 1 +# Build 2 +Write-Host "This utility is obsolete and may not work. Please use control.pyw" $root = $PSCommandPath | Split-Path -Parent -. $root\config.ps1 +$server = (Get-Content "$root\configpcm.ini" | select -Skip 1 | ConvertFrom-StringData).server +$resources = (Get-Content "$root\configpcm.ini" | select -Skip 1 | ConvertFrom-StringData).resources + function seeGroups { $answer = Invoke-RestMethod -Method Get -Uri "$server/get/groups" @@ -255,7 +258,7 @@ function menuCooks { $CookName = Read-Host -Prompt "Nombre de la receta a añadir" seeGroups #Function $GroupName = Read-Host -Prompt "Nombre del grupo que quieres que lo tenga" - Invoke-RestMethod -Method Get -Uri "$server/set/cook?GroupName=$GroupName&CookName=$CookName" + Invoke-RestMethod -Method Get -Uri "$server/add/cookgrp?GroupName=$GroupName&CookName=$CookName" pause } '5' { diff --git a/client/control.pyw b/client/control.pyw new file mode 100644 index 0000000..51306d1 --- /dev/null +++ b/client/control.pyw @@ -0,0 +1,199 @@ +#!/usr/bin/python3 +# Version 0.5 +import easygui +import configparser +import json +import requests +import subprocess, platform + +def ping(host): + """ + Returns True if host (str) responds to a ping request. + Remember that a host may not respond to a ping (ICMP) request even if the host name is valid. + """ + # Option for the number of packets as a function of + param = '-n' if platform.system().lower()=='windows' else '-c' + # Building the command. Ex: "ping -c 1 google.com" + command = ['ping', param, '1', host] + return subprocess.call(command) == 0 + +## sendmenudot, returns of choice of a series of choices that has "." as delimiter inside it (For returning index) +def sendmenudot(text,title,choices): + menu = easygui.choicebox(text,title,choices) + if menu is not None: + return menu.split('.')[0] + else: + return menu +## showchoicesapi, apiurl has to have all (Inlcuding server part) +def showchoicesapi(text,title,apiurl,field='Name'): + try: + jsonobj = json.loads(requests.get(apiurl).text) + listitems = [] + for ite in jsonobj: + listitems.append(ite[field]) + return easygui.choicebox(text,title,listitems) + except: + easygui.msgbox(msg='Error en la aplicación al consultar', title=title, ok_button='OK') + return None + +def sendsettoapi(suburl,goodtext): # Send a add/del/modify to API. Doesn't return more than "Valid" and "Not valid" + global c_server + try: + jsonobj = json.loads(requests.get(c_server+suburl).text) + if jsonobj is not None and jsonobj['RESULT'] is not None and jsonobj['RESULT'] == 'ERROR': + if jsonobj['TEXT']: + easygui.msgbox(jsonobj['TEXT'],"Error","Ok") + else: + easygui.msgbox("Error no especificado","Error","Ok") + return 1 + else: + easygui.msgbox(goodtext,"Exito","Ok") + return 0 + except: + easygui.msgbox("Error inesperado","Error","Ok") + return 1 + + +config = configparser.ConfigParser() +config.read('configpcm.ini') + +c_server = config['General']['server'] +c_resources = config['General']['resources'].replace('\\\\','\\') + + +def menuprinc(): + menu = sendmenudot("Menú principal","Seleccione opción",['1.Equipos','2.Grupos','3.Recetas','4.Reportes/Listados']) + if menu == '1': + mcomputers() + elif menu == '2': + mgroups() + elif menu == '3': + mcooks() + elif menu == '4': + mreports() + +def mcomputers(): + global c_server + menu = sendmenudot("Menú equipos","Seleccione opción",['1.Añadir equipo','2.Borrar equipo','3.Añadir equipo a un grupo','4.Listado de equipos']) + + if menu == '1': + newcomputer = easygui.enterbox("Nombre del nuevo equipo","Añadir equipo") + if easygui.ynbox("Nombre del equipo: '"+newcomputer+"'. ¿Seguro?","Añadir equipo"): + sendsettoapi("/add/computer?ComputerName="+newcomputer,"Equipo añadido a la base de datos") + elif menu == '2': + whatc = showchoicesapi("Seleccione equipo a borrar","Borrar equipo",c_server+"/get/computers") + if whatc is not None and easygui.ynbox("Se va a borrar el equipo '"+whatc+"'. ¿Seguro?","Borrar equipo"): + sendsettoapi("/del/computer?ComputerName="+whatc,"Equipo borrado") + elif menu == '3': + m_addcomputergroup() + elif menu == '4': + showchoicesapi("Lista de equipos","Listado de equipos",c_server+"/get/computers") + elif menu is None: + menuprinc() + return + mcomputers() + +def mgroups(): + global c_server + menu = sendmenudot("Menú grupos","Seleccione opción",['1.Añadir grupo','2.Borrar grupo','3.Añadir equipo a un grupo','4.Quitar equipo de un grupo','5.Listado de grupos','6.Listado de equipos de un grupo']) + if menu == '1': + newgroup = easygui.enterbox("Nombre del nuevo grupo","Añadir grupo") + if easygui.ynbox("Nombre del grupo: '"+newgroup+"'. ¿Seguro?","Añadir grupo"): + sendsettoapi("/add/group?GroupName="+newgroup,"Grupo añadido a la base de datos") + elif menu == '2': + group = showchoicesapi("Lista de grupos. Selecciona uno para borrarlo","Borrar grupo",c_server+"/get/groups",'Name') + if group is not None: + if easygui.ynbox("Borrar grupo '"+group+"'. ¿Seguro?","Borrar grupo"): + sendsettoapi("/del/group?GroupName="+group,"Grupo borrado") + elif menu == '3': + m_addcomputergroup() + elif menu == '4': + m_delcomputergroup() + elif menu == '5': + showchoicesapi("Listado de grupos","Listado de grupos",c_server+"/get/groups",field='Name') + elif menu == '6': + group = showchoicesapi("Lista de grupos","Listado de equipos de un grupo",c_server+"/get/groups") + showchoicesapi("Lista de equipos del grupo "+group,"Listado de equipos de un grupo",c_server+"/get/computersgrp?GroupName="+group) + elif menu is None: + menuprinc() + return + mcomputers() + +def mcooks(): + global c_server + global c_resources + menu = sendmenudot("Menú recetas","Seleccione opción",['1.Detalles de una receta','2.Renombrar receta','3.Añadir receta a un grupo','4.Eliminar receta de un grupo','5.Grupos de una receta']) + if menu == '1': + cook = showchoicesapi("Lista de recetas implementadas en algún grupo. Selecciona una para ver sus datos","Detalles de una receta",c_server+"/get/cookall",'CookName') + if cook is not None: + showchoicesapi("Lista de grupos de la receta "+cook,"Listado de grupos de una receta",c_server+"/get/grpcook?CookName="+cook) + elif menu == '2': + cook = showchoicesapi("Lista de recetas implementadas en algún grupo. Selecciona una para renombrarla. Si la receta no está en ningún grupo, se puede renombrar desde fuera.","Renombrar receta",c_server+"/get/cookall",'CookName') + if cook is not None: + newname = easygui.enterbox("Nombre de la nueva receta","Renombrar receta") + if newname is not None and easygui.ynbox("Cambiar nombre de la receta '"+cook+"' por '"+newname+"'. ¿Seguro?","Renombrar receta"): + sendsettoapi("/upd/cookname?CookName="+cook+"&CookNewName="+newname,"Receta cambiada de nombre") + elif menu == '3': + cook = easygui.enterbox("Nombre de la receta","Añadir receta a un grupo") + group = showchoicesapi("Lista de grupos existentes","Listado de grupos",c_server+"/get/groups") + if easygui.ynbox("Añadir la receta "+cook+" al grupo '"+group+"'. ¿Seguro?","Añadir receta a un grupo"): + sendsettoapi("/add/cookgrp?CookName="+cook+"&GroupName="+group,"Receta añadida al grupo") + elif menu == '4': + cook = showchoicesapi("Lista de recetas implementadas en algún grupo. Selecciona una para elegir un grupo de esa receta para borrar. Si la receta no está en ningún grupo, no saldrá.","Eliminar receta de un grupo",c_server+"/get/cookall",'CookName') + if cook is not None: + group = showchoicesapi("Lista de grupos que tienen esa receta. Selecciona el grupo a quitar","Eliminar receta de un grupo",c_server+"/get/grpcook?CookName="+cook,'Name') + if group is not None and easygui.ynbox("Se va a quitar la receta "+cook+" del grupo "+group+". ¿Seguro?","Eliminar receta de un grupo"): + sendsettoapi("/del/cookgrp?CookName="+cook+"&GroupName="+group,"Receta quitada del grupo") + elif menu == '5': + cook = showchoicesapi("Lista de recetas implementadas en algún grupo. Selecciona una para elegir un grupo de esa receta para borrar. Si la receta no está en ningún grupo, no saldrá.","Eliminar receta de un grupo",c_server+"/get/cookall",'CookName') + if cook is not None: + showchoicesapi("Lista de grupos que tienen esa receta. Selecciona el grupo a quitar","Eliminar receta de un grupo",c_server+"/get/grpcook?CookName="+cook,'Name') + elif menu is None: + menuprinc() + return + + mcooks() + +def mreports(): + global c_server + global c_resources + menu = sendmenudot("Menú reportes","Seleccione opción",['1.Listado de equipos','2.Listado de grupos','3.Equipos y recetas de un grupo','4.Grupos de una receta']) + + if menu == '1': #Listado de equipos + showchoicesapi("Lista de equipos","Listado de equipos",c_server+"/get/computers") + elif menu == '2': #Listado de grupos + showchoicesapi("Lista de grupos","Listado de grupos",c_server+"/get/groups") + elif menu == '3': + group = showchoicesapi("Lista de grupos","Listado de grupos",c_server+"/get/groups") + showchoicesapi("Lista de equipos del grupo "+group,"Listado de equipos y recetas de un grupo",c_server+"/get/computersgrp?GroupName="+group) + showchoicesapi("Lista de recetas del grupo "+group,"Listado de equipos y recetas de un grupo",c_server+"/get/cookgrp?GroupName="+group,'CookName') + elif menu == '4': + cook = showchoicesapi("Lista de recetas con grupo. Selecciona una para ver sus grupos","Listado de grupos de una receta",c_server+"/get/cookall",'CookName') + if cook is not None: + showchoicesapi("Lista de grupos de la receta "+cook,"Listado de grupos de una receta",c_server+"/get/grpcook?CookName="+cook) + elif menu is None: + menuprinc() + return + mreports() + + +def m_addcomputergroup(): + whatc = howchoicesapi("Seleccione equipo a añadir a un grupo","Añadir Equipo a grupo",c_server+"/get/computers") + if whatc is None: + return + whatg = showchoicesapi("Seleccione grupo a ser añadido","Añadir Equipo a grupo",c_server+"/get/groups") + if whatg is None: + return + sendsettoapi("/add/grpcomputer?ComputerName="+whatc+"&GroupName="+whatg,"Equipo añadido al grupo") + +def m_delcomputergroup(): + group = showchoicesapi("Seleccione grupo del que quitar un equipo","Borrar equipo de un grupo",c_server+"/get/groups") + if group is None: + return + computer = showchoicesapi("Seleccione grupo a ser quitado","Borrar equipo de un grupo",c_server+"/get/computersgrp?GroupName="+group) + if computer is None: + return + if easygui.ynbox("Quitar el equipo "+computer+" del grupo '"+group+"'. ¿Seguro?","Borrar equipo de un grupo"): + sendsettoapi("/del/grpcomputer?ComputerName="+computer+"&GroupName="+group,"Equipo quitado del grupo") + +menuprinc() diff --git a/sysopt.py b/sysopt.py index ac3eff9..ba71be9 100644 --- a/sysopt.py +++ b/sysopt.py @@ -1,3 +1,3 @@ addComputers = False # False/True accept auto add computers that start client program and connect to server -debugsql = True # True is show in log all SQL queries +debugsql = False # True is show in log all SQL queries