# matthew ragan
import os
import subprocess
class WindowsUtilities:
'''The WindowsUtilities class is used to easily access a number of utility functions for Windows 10.
Robocopy
Address the need to sync folders between machines or from one location to another.
This method exits as a single Popen call allowing the user to use these in a for loop
and effectivly start multiple simultanious copies frome one machine to many.
Use cases:
* syncing media or software directories to multiple machines
* copying media or software from an external drive to a local drive
* copying media or software from a local drive to an external drive
Shutdown
Addresses the need to shutdown or restart a machine. This could be used on a local
or remote machine.
Use cases:
* Regular machine resets
* Scheduled maintenace machine shutdown or restarts
* Ability to shutdown or reboot a remote machine
StartStopService
Addresses the need to start or stop a windows service on a remote machine.
RestartService
Addreses the need to restart a windows service on a remote machine.
'''
def __init__(self):
self.WindowsProjectFolder = project.folder
self.ProjectLogs = "{}/logs".format( project.folder )
self.ResetStopDelay = 500
self.ResetStartDelay = 3000
print("WindowsUtilities Init")
return
def Robocopy( self, targetUri, logName, sourceFolder, destinationFolder, xDir = '', xFile ='', debug=False ):
'''Start a generic robocopy job on Windows to syncronize the contents of folders
Notes
---------
Args
---------
targetUri (str) : the target URI is a string used to format the log file, you may choose to make this
the machine name, or ip-address for a given target. This parameter is used to ensure you have
reliable and parsable logs for each copy event you start.
sourceFolder (directory) : this is the target source folder your are copying from. This can be located
on a local machine, a network machine, or external drive. Paths are correctly converted to windows
os style formatting, so you're free to use "c:/someFolder/" as your format. This means you
can also use project.folder() if you so choose.
Expected format:
network path - "\\server\c$\someFolder"
local path - "D:/001-source"
destinationFolder (directory) : this is the destination folder you are copying to. You can use a network
path to target remote machines. You may want to use the administrator share, formatted
as: "\\server\c$\someFolder". For both source and destination folder no additional special
formatting should be necessary.
Expected format:
network path - "\\server\c$\someFolder"
local path - "D:/001-source"
xDir (str) : the path to any directories you would like to exclude formated as a string. This should
be relative to the source folder.
xFile (str) : the string name of a file with file extension that you wish to exclude from the copy
process.
logName (str) : this is the log type name that will be used to create a directory for your log files.
This allows the reuse of this function to create logs for any number of copy processes.
Returns
---------
copyProcess (Popen Object) : this is the Popen object that's running the copy process. This can be
used to check or kill the process if necessary.
'''
# checks for network file paths in source and destination paths
if "\\" in sourceFolder[:1]:
sourceFolder = "\\" + sourceFolder
else:
pass
if "\\" in destinationFolder[:1]:
destinationFolder = "\\" + destinationFolder
else:
pass
# assemble all of the necessary pieces for a copy
windowsProjectFolder = (self.WindowsProjectFolder).replace('/', '\\')
source = (sourceFolder).replace('/', '\\')
dest = (destinationFolder).replace('/', '\\')
exFile = (xFile).replace('/', '\\')
exDir = (xDir).replace('/', '\\')
logs = '{projectLogs}/robocopy/{name}/'.format( projectLogs = self.ProjectLogs, name = logName )
logs = (logs).replace('/', '\\')
logFile = '{log}{uri}.txt'.format(log = logs, uri = targetUri)
log = '/LOG+:{logF}'.format(logF = logFile)
mirFlag = '/MIR'
mtFlag = '/MT:12'
rFlag = '/R:0'
wFlag = '/W:1'
xfFlag = xFile
xdFlag = xDir
# format the command list for Popen
copyCommand = [ "ROBOCOPY",
source,
dest,
log,
mirFlag,
mtFlag,
rFlag,
wFlag,
'/XF',
xfFlag,
'/XD',
xdFlag]
# create a directory for logs if none exists
if not os.path.exists(logs):
os.makedirs(logs)
# delete old log files
if os.path.isfile(logFile):
os.remove(logFile)
else:
pass
# if debugging, print out all the args passed to the command line
if debug:
print(copyCommand)
else:
pass
# use subprocess to start a copy command
copyProcess = subprocess.Popen(copyCommand)
return copyProcess
def Shutdown(self):
return
def StartStopService(self, targetServerIp = None, serviceName = None, running=False, reset=False):
'''Used start and stop services on remote machines.
Notes
---------
Used to issue start and stop commmands for monsvc on remote machines without running
and external CMD or BAT file. Similar to the robocopy commands these issue system
level commands via Windows. This will start or stop monsvc on available remote machines
briging much of the monsvc functionality into single UI deployments. Be advsied that
using this will stop / start all of the processes associated with a given monsvc config.
As a note reset is a reserved argument for future devleopment and is not currently in use.
# sudo code:
call(['sc', 'server_target', 'stop', 'monsvc'])
Args
---------
location (str) : a string entry that determins which server list is used to issue commands
running (bool) : a boolean that describes if the issued monsvc command should be start or stop
reset (bool) : a boolean that will first issue a stop command followed by a start command
Returns
---------
none
'''
machine_ip = op.Project.fetch('local_config')['ip_address']
svcCmd = "start" if running else "stop"
# error handling
if targetServerIp == None:
print("No Server Specified - Not issueing Service Control Commands")
pass
else:
# resetting the service will first issue stop commands to the service
# followed by a start command
if reset:
self.RestartService(targetServerIp, serviceName)
# in the case that reset is False, just issue a stop or start command
# to the target server, with the target service name
else:
cmdList = [ "sc",
'\\\{ip}'.format(ip = server),
svcCmd,
serviceName]
subprocess.Popen(monsvc_command)
return
def RestartService(self, targetServerIp, serviceName):
'''Used to stop and start touch on a single server.
Notes
---------
Used to issue start and stop commmands for monsvc on remote machines without running
and external CMD or BAT file. Similar to the robocopy commands these issue system
level commands via Windows. This will start or stop monsvc on available remote machines
briging much of the monsvc functionality into single UI deployments.
# sudo code:
call(['sc', 'server_target', 'stop', 'monsvc'])
Args
---------
target_server (str) : a string entry that determins which server list is used
to issue commands
Returns
---------
none
'''
stopCommand = [ "sc",
'\\\{ip}'.format(ip = targetServerIp),
"stop",
serviceName]
startCommand = [ "sc",
'\\\{ip}'.format(ip = targetServerIp),
"start",
serviceName]
delayScript = '''
import subprocess
subprocess.Popen(args[0])'''
run(delayScript, stopCommand, delayMilliSeconds = self.ResetStopDelay)
run(delayScript, startCommand, delayMilliSeconds = self.ResetStartDelay)
return