Just looking to see if my assessment is correct on the easiest way to do this.
Background: we have a software suite which we're running via services. The services need to be shut down between 10pm and 6am. We also need to check every 10 min to recover the services in case they've gone down when they were supposed to be up, and in case we restarted during a time when they'd need to be shut down.
Choices: I'm thinking a scheduled task that runs a WSH / JScript every 10 minutes. The pseduo code for the JScript would be something like:
Get current time
If the current time is after 10pm but before 6am
call EnsureDowntime()
Else
call EnsureUptime()
EnsureDownime():
If the services are running, stop them
EnsureUptime():
If the services are not running, start them
Questions:
Is Scheduled Task / WSH / JScript the best way to do this, or at least an acceptable way?
is calling a new Date() and using .getHours() the best why to find the time?
Does my pseudo-code look like the best way to approach this?
how can I detect the state of a service (running / non-running)?
or in this case, should I just start / stop the services regardless of state? If there are no errors and it won't re-start a service that's already started, I imagine I could just use "net start" and "net stop".
Thanks in advance for any help you can give!
Figured it out!
Solution Walkthrough
Firstly, create a scheduled task and point it to a .bat file (pointing it directly to a .js will not work, I discovered).
In the batch file point to your .js file to run it (for example, mine was located in "D:\BF_Ping.js"
BF_Ping.bat:
D:
cd\
BF_Ping.js
Next, create your JavaScript file. My fully commented file is below for reference:
BF_Ping.js:
//*** Time Variables ***//
var UptimeBeginsAtHour = 6; // Hour at which the services should be started
var UptimeEndsAtHour = 22; // Hour at which the services should be ended
//*** Flags ***//
var KILL_FLAG = FindFile("C:\\BF_KILL.txt"); // If this flag is true, services will be shut down and not started.
var LAZY_FLAG = FindFile("C:\\BF_LAZY.txt"); // If this flag is true, nothing in the script will run.
if (KILL_FLAG == true)
{
StopBizFlowServices();
}
if (KILL_FLAG == false && LAZY_FLAG == false)
{
DetectTimeAndProcess(UptimeBeginsAtHour, UptimeEndsAtHour);
}
/***
* DetectTimeAndProcess(startAtHour, stopAtHour):
* Starts or stops BizFlow Services based on uptime variables.
*
* ---Arguments---
* startAtHour Hour after which services should be started (defined by variable in main code)
* stopAtHour Hour after which services should be started (defined by variable in main code)
*
* ---Returns---
* None (void)
*
* ---Called By---
* Main code.
*/
function DetectTimeAndProcess(startAtHour, stopAtHour)
{
var currentTime = new Date();
var hour = currentTime.getHours(); // Returns Hour in 24-hour format
if (hour > startAtHour && hour < stopAtHour)
{
StartBizFlowServices();
}
else
{
StopBizFlowServices();
}
}
/***
* StartBizFlowServices():
* Starts BizFlow Services using "net start" and the service name.
*
* --- Arguments ---
* None.
*
* --- Returns ---
* None (void)
*
* --- Called By---
* DetectTimeAndProcess()
*/
function StartBizFlowServices()
{
var objShell = WScript.CreateObject("WScript.Shell");
objShell.Run("net start \"BizFlow Database Service\" ", 1, true);
objShell.Run("net start \"BizFlow Main Service\"", 1, true);
objShell.Run("net start \"BizFlow OLE-DB Service\"", 1, true);
objShell.Run("net start \"BizFlow PKI Service\"", 1, true);
objShell.Run("net start \"BizFlow Queue Service\"", 1, true);
objShell.Run("net start \"BizFlow Scheduler Service\"", 1, true);
}
/***
* StopBizFlowServices():
* Stops BizFlow Services using "net stop" and the service name.
*
* --- Arguments ---
* None.
*
* --- Returns ---
* None (void)
*
* --- Called By---
* DetectTimeAndProcess()
*/
function StopBizFlowServices()
{
var objShell = WScript.CreateObject("WScript.Shell");
objShell.Run("net stop \"BizFlow OLE-DB Service\"", 1, true);
objShell.Run("net stop \"BizFlow PKI Service\"", 1, true);
objShell.Run("net stop \"BizFlow Queue Service\"", 1, true);
objShell.Run("net stop \"BizFlow Scheduler Service\"", 1, true);
objShell.Run("net stop \"BizFlow Main Service\"", 1, true);
objShell.Run("net stop \"BizFlow Database Service\"", 1, true);
}
/***
*
* FindFile (filePath):
* Searches for the existence of a given file path.
*
* --- Arguments ---
* filePath Full Path of file (including file name)
*
* --- Returns ---
* true if file is found
* false if file is not found
*
* --- Called By---
* Main Code (while setting flags)
*/
function FindFile(filePath)
{
var fso; //For FileSystemObject
fso = new ActiveXObject("Scripting.FileSystemObject");
if(fso.FileExists(filePath))
{
return true;
}
else
{
return false;
}
}
I'm actually a little proud of this one -- it ran without error the first time :)
Some Notes:
Times are given in 24 hour format.
Set the scheduled task to run every 5-10 minutes to ensure that the check is performed often.
The services I use here are BizFlow services, a product that a client is using. You can change the names of the functions accordingly, of course.
Note the escape characters for the quotes when running "net start" or "net stop" for the service name.
Use the full service name, as it appears in services.msc.
Please let me know if you have any questions or comments on how I might improve the code!
Thanks,
--
Sean
You mentioned that:
... (pointing it directly to a .js will not work, I discovered)
That was something I'd run into as well when trying to run a WSH JS file from Task Scheduler in Windows 2003. I found in WIn7, you could run it directly with just a reference to the .js file, but it failed to pass parameters at least to the script if run the same way in the 2003 Scheduler.
I did find a workaround that seems to work to avoid needing the stub batch file that you mentioned though: if you just prefix the .js file with 'WScript' (or 'CScript') -- no full path should be needed because those are in %WINDIR%\system32, then it seems to work for me including the .js file getting any parameters passed.
Related
Background
I had to apply the recovery script (https://github.com/splitbrain/dokuwiki-recover, 93fce60, 31. Aug. 2020) because the wiki did not work after installation of plugin "code3".?
I followed the instructions that appeared when executing the script, in which one step were saying:
Change the superuser configuration back to contain your usual admin account
Question
How to perform the mentioned step ("Change the superuser configuration back to contain your usual admin account")?
In order to perform the mentioned step ("Change the superuser configuration back to contain your usual admin account"), the file local.php in folder "/config" has to be changed to
<?php
/**
* Dokuwiki's Main Configuration File - Local Settings
* Auto-generated by install script
* Date: ...
*/
$conf['title'] = '<YourDokuWikiName>';
$conf['lang'] = '<YourLanguage>';
$conf['license'] = '<YourDokuWikiLicense>';
$conf['useacl'] = 1;
$conf['superuser'] = '#admin';
$conf['disableactions'] = 'register';
...by removing the lines that have been added by the recover script
// The following lines were added be dokuwiki-recover
// 2021-...
$conf['useacl'] = 1;
$conf['authtype'] = 'authplain';
$conf['superuser'] = '#dokuwiki-recover';
$conf['userewrite'] = 0;
$conf['lang'] = 'en';
$conf['template'] = 'dokuwiki';
The obvious reason is that the last php assignment wins, and hence...
$conf['superuser'] = '#dokuwiki-recover';
...made all users of group dokuwiki-recover to adminintrators.
This may sound duplicate but it is not.
I know that I can use the below configuration in config file and start multiple instance of the chrome driver that would run the features in parallel that share the step definitions.
capabilities: {
'browserName': 'chrome',
'shardTestFiles': true,
'maxInstances': 0
},
Q1. But my question is around why the chromedriver doesn't exit when a scenario fails?(That happens only when I use value of maxInstance > 0 ).
The chromedriver exit with exitcode- 3 and exitcode- 1.
Q2. Is anyone able to sort out the reporting issue? How can I generate the report when all the features have finished?
Any sort of help will be appreciated?
Thanks
In order to generate the consolidated html report after parallel run,I have used afterLaunch parameter in the protractor.conf.js file and have used https://github.com/gkushang/cucumber-html-reporter. Below is the code-
afterLaunch: function afterLaunch () {
var cucumberHtmlReporter = require('cucumber-html-reporter');
var jsonReportFolder = '/path/to/all/json/reports';
var cucumberhtmlReport = path.join(jsonReportFolder, cucumber.html');
var options = {
theme: 'bootstrap',
jsonDir: jsonReportFolder,
output: cucumberhtmlReport,
reportSuiteAsScenarios: true,
launchReport: true
};
cucumberHtmlReporter.generate(options);
}
The existing behavior is correct.Do not use 'maxInstances': 0
The default value is 1 and any value>1 is the right way to do it. The error that you are seeing is because thats how the source code - taskScheduler
They are handling shard tests in this taskScheduler exports and logic of maxinstances is as below
this.maxInstance = capabilities.maxInstances || 1;
/**
* Get maximum number of concurrent tasks required/permitted.
*
* #return {number}
*/
count += Math.min(queue.maxInstance, queue.specLists.length);
So if you have maxInstances 0, It will cause problems and your code will never exit cleanly. Also I dont think your code will run in parallel
What I would suggest is
Check your protractor version and update to latest
Change your config file to - 'maxInstances': 3 //anything greater than 1. 1 is default
Technically I can install cron on the machine and curl the url, but I'm trying to avoid that. Any way to accomplish this?
Reason I want to avoid cron is so I can easily change the schedule or stop it completely without also ssh'ing into the machine to do so.
Take a look at: https://github.com/enragedginger/akka-quartz-scheduler.
Refer to http://quartz-scheduler.org/api/2.1.7/org/quartz/CronExpression.html for valid CronExpressions and examples.
An example taken from the docs:
An example schedule called Every-30-Seconds which, aptly, fires-off every 30 seconds:
akka {
quartz {
schedules {
Every30Seconds {
description = "A cron job that fires off every 30 seconds"
expression = "*/30 * * ? * *"
calendar = "OnlyBusinessHours"
}
}
}
}
You can integrate this into your Play! application (probably in your Global application)
You can use the Akka scheduler.
val scheduler = Akka.system(app).scheduler
scheduler.schedule(0 seconds, 1 hour) {
// run this block every hour
}
The first parameter is a delay, so if you wanted to delay to a specific time you could easily calculate the target time with some simple date arithmetic.
Check out https://github.com/philcali/cronish
Some example code from README.md:
val payroll = task {
println("You have just been paid... Finally!")
}
// Yes... that's how you run it
payroll executes "every last Friday in every month"
val greetings = job (println("Hello there")) describedAs "General Greetings"
// give a delayed start
val delayed = greetings runs "every day at 7:30" in 5.seconds
// give an exact time to start
val exact = greetings runs "every day at noon" starting now + 1.week
// resets a job to its definition
val reseted = exact.reset()
reseted starting now + 1.day
If I call ecrt_slave_config_reg_pdo_entry to create a domain offset, I get the message:
Failed to register PDO entry: No such file or directory
I believe that I have to call ecrt_slave_config_pdos, which I am not at the moment. However, I do not understand why I have to call it. In my case, the slave I like to talk to is already connected to the bus. If I enter ec cstruct -p [SLAVE_POS] in a terminal, I get the PDO layout.
me#here:~$ ec cstruct -p 1
/* Master 0, Slave 1, "..."
* Vendor ID: 0x...
* Product code: 0x...
* Revision number: 0x...
*/
ec_pdo_entry_info_t slave_1_pdo_entries[] = {
{0x6040, 0x00, 16},
...
};
ec_pdo_info_t slave_1_pdos[] = {
{0x1600, 2, slave_1_pdo_entries + 0},
...
};
ec_sync_info_t slave_1_syncs[] = {
{0, EC_DIR_OUTPUT, 0, NULL, EC_WD_DISABLE},
...
{0xff}
};
Why can I get all input arguments for ecrt_slave_config_pdos from the terminal command, but EtherCat cannot (or does not want to) configure the slave itself? Can the slave configuration be automated?
You can use the functions ecrt_master_get_slave(), ecrt_master_get_sync_manager(), ecrt_master_get_pdo(), and (of course) ecrt_master_get_pdo_entry() to retrieve all the necessary information. ecrt_master_get_sync_manager(), e.g., will not set the PDO information, but will return the number of PDO positions for a synchronization manager.
So something like that works for me (error code checks left out for the sake of clarity):
ecrt_master_get_slave(..., &slaveInformation);
// For every synchronization manager...
for (size_t syncIndex = 0; syncIndex < slaveInformation.sync_count; ++syncIndex) {
ecrt_master_get_sync_manager(..., &syncInformation);
ecrt_slave_config_sync_manager(...);
// For every PDO field...
for (size_t pdoIndex = 0; pdoIndex < syncInformation.n_pdos; ++pdoIndex) {
ecrt_master_get_pdo(..., &pdoInformation);
ecrt_slave_config_pdo_assign_add(...);
// For every entry in the pdo field...
for (size_t entryIndex = 0; entryIndex < pdoInformation.n_entries; ++entryIndex) {
ecrt_master_get_pdo_entry(..., &entryInformation);
ecrt_slave_config_pdo_mapping_add(...);
}
}
}
This, however, does not really answer the question why (or if) all this is necessary. To be specific on cleaner: It would be nice, if the Pyramid of Doom could be avoided. Especially, since I am telling the slave something it told me one line before.
I have a Windows Service that uses Quartz.NET to execute jobs that are scheduled. I only want it to pick up a single job at a time. However, occasionally I am seeing behavior that indicates that it has picked up two jobs at once.
There are two log files (the regular one and one automatically generated when the regular one is in use) with jobs that start at the exact same time. I can see both jobs executing in the QRTZ_FIRED_TRIGGERS table, but only one has the correct instance ID, which is odd.
I have configured Quartz to use only a single thread. Is this not how you tell it to only pick up a single job at a time?
Here is my quartz.config file with sensitive values hashed out:
quartz.scheduler.instanceName = DefaultQuartzJobScheduler
quartz.scheduler.instanceId = ######################
quartz.jobstore.clustered = true
quartz.jobstore.clusterCheckinInterval = 15000
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.jobStore.useProperties = false
quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.OracleDelegate, Quartz
quartz.jobStore.tablePrefix = QRTZ_
quartz.jobStore.lockHandler.type = Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz
quartz.jobStore.misfireThreshold = 60000
quartz.jobStore.dataSource = default
quartz.dataSource.default.connectionString = ######################
quartz.dataSource.default.provider = OracleClient-20
# Customizable values per Node
quartz.threadPool.threadCount = 1
quartz.threadPool.threadPriority = Normal
Make the threadcount = 1.
<add key="quartz.threadPool.threadCount" value="1"/>
<add key="quartz.threadPool.threadPriority" value="Normal"/>
(as you have done)
Make each of your jobs "Stateful"
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class StatefulDoesNotRunConcurrentlyJob : IJob /* : IStatefulJob */ /* Error 43 'Quartz.IStatefulJob' is obsolete: 'Use DisallowConcurrentExecutionAttribute and/or PersistJobDataAfterExecutionAttribute annotations instead. */
{
}
I've left in the name of the ~~older~~ version of how to do this (namely, the "IStatefulJob") and the error message that is generated when you code to the outdated "IStatefulJob" interface. But the error message gives the hint.
Basically, if you have 1 thread AND every job is marked with "DisallowConcurrentExecution", it should result in 1 job at any given time..running in "serial mode".