How to automate synchronizing Windows 10 guest's time with the Linux host? - kvm

On Arch Linux I have a Windows 10 Guest on top of libvirt, kvm and virsh (still having some trouble to connect all these dots mentally together). Every time I suspend the laptop and a day is gone the Windows 10 host goes out of sync. I learned that with the following command I can force a time sync in the host:
➜ ~ virsh qemu-agent-command win10 '{"execute":"guest-set-time"}'
{"return":{}}
In order to make this work I modifed the clock XML block and added a kvm clock entry. This is how the block looks like now:
<clock offset="localtime">
<timer name="tsc" tickpolicy="delay"/>
<timer name="kvmclock"/>
<timer name="rtc" tickpolicy="delay" track="wall"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="yes"/>
</clock>
I would like to know whether I can automate this step or trigger an update everytime I wake up the machine or log-in.
Thanks in advance

I was not able to get anywhere specifically using virsh. Here is how I fixed this issue in a Windows 11 guest on MacOS in UTM 3.6.4 and 4.1.5.
At first I tried many workarounds using w32tm - but this was always flaky.
This helped slightly:
disable "use local time for base clock" (otherwise you can't add a manual -rtc argument if using UTM)
add -rtc base=localtime,driftfix=slew
This wasn't great, because it won't recover a significant delta.
This is the solution I settled on (run in the Windows guest). It creates a scheduled task that runs every 5 minutes, gets the time from NTP, converts it to local time, measures the drift, and if the drift is >30 seconds in either direction it updates the system clock.
function Get-NtpTime
{
[OutputType([datetime])]
[CmdletBinding()]
param
(
[string]$Server = "time.nist.gov",
[int]$Port = 13
)
if (-not $PSBoundParameters.ContainsKey('ErrorAction'))
{
$ErrorActionPreference = 'Stop'
}
$Client = [Net.Sockets.TcpClient]::new($Server, $Port)
$Reader = [IO.StreamReader]::new($Client.GetStream())
try
{
$Response = $Reader.ReadToEnd()
$UtcString = $Response.Substring(7, 17)
$LocalTime = [datetime]::ParseExact(
$UtcString,
"yy-MM-dd HH:mm:ss",
[cultureinfo]::InvariantCulture,
[Globalization.DateTimeStyles]::AssumeUniversal
)
}
finally
{
$Reader.Dispose()
$Client.Dispose()
}
$LocalTime
}
function Register-TimeSync
{
[CmdletBinding()]
param
(
[Parameter()]
[timespan]$RepetitionInterval = (New-TimeSpan -Minutes 5),
[Parameter()]
[timespan]$ExecutionTimeLimit = (New-TimeSpan -Minutes 3)
)
$Invocation = {
$NtpTime = Get-NtpTime
$Delta = [datetime]::Now - $NtpTime
if ([Math]::Abs($Delta.TotalSeconds) -gt 30)
{
Set-Date $NtpTime
}
}
$PSName = if ($PSVersionTable.PSVersion.Major -le 5) {'powershell'} else {'pwsh'}
$Path = (Get-Command $PSName).Source
$Command = Get-Command Get-NtpTime
$Definition = "function Get-NtpTime`n{$($Command.Definition)}"
$Invocation = $Definition, $Invocation -join "`n"
$Bytes = [Text.Encoding]::Unicode.GetBytes($Invocation)
$Encoded = [Convert]::ToBase64String($Bytes)
$TriggerParams = #{
Once = $true
At = [datetime]::Today
RepetitionInterval = $RepetitionInterval
}
$Trigger = New-ScheduledTaskTrigger #TriggerParams
$Action = New-ScheduledTaskAction -Execute $Path -Argument "-NoProfile -EncodedCommand $Encoded"
$Settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $ExecutionTimeLimit -MultipleInstances IgnoreNew
$Principal = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
$RegisterParams = #{
TaskName = "Update system time from NTP"
Trigger = $Trigger
Action = $Action
Settings = $Settings
Principal = $Principal
Force = $true
}
Register-ScheduledTask #RegisterParams
}
Usage (run as admin):
Register-TimeSync

Related

How can I pass a value from a TeamCity failure Condition to an e-mail notification?

I want to show the status of the build in an e-mail with just a simple FAIL or PASS text appear in the body. There does not seem to be any kind of predefined "buildStatus" variable that I can access or setup in TeamCity. I guess I need to access the "failureConditions" function at bottom but not sure how, tried lots of things but nothing worked, this is my script:
package _Self.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnText
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnText
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule
allowExternalStatus = true
params {
param("MinorVersion", "0")
param("RevisionVersion", "0")
}
powerShell {
name = "Email"
scriptMode = script {
content = """
function Send-ToEmail([string]${'$'}email, [string]${'$'}attachmentpath){
${'$'}message = new-object Net.Mail.MailMessage;
${'$'}message.From = "teamcity#blog.com";
${'$'}message.To.Add(${'$'}email);
${'$'}message.Subject = "%env.TEAMCITY_PROJECT_NAME% | %VersionNumber% ";
${'$'}message.Body = "The build: PASS or FAIL text here";
${'$'}smtp = new-object Net.Mail.SmtpClient("blog.local", "25");
${'$'}smtp.EnableSSL = ${'$'}true;
${'$'}smtp.send(${'$'}message);
write-host "Mail Sent" ;
}
Send-ToEmail -email "me#blog.com" -attachmentpath ${'$'}path;
""".trimIndent()
}
}
}
failureConditions {
failOnText {
conditionType = BuildFailureOnText.ConditionType.CONTAINS
pattern = "FAIL"
reverse = false
}
}
To solve this in T/C:
Create new custom env. var. e.g. MyBuildStatus
Create new build step e.g. Set build status (only executes if build successful)
Create a new Parameter name='env.MyBuildStatus.Status' value='SUCCESS'
Add variable to email subject: $message.Subject = "%env.MyBuildStatus.Status%
Why T/C has no build in "buildstatus" env. variable is interesting.

DSC Script Resource - executes .exe, but doesn't wait until completion

Question)
How do I get a DSC script resource to wait until the code has completed before moving on?
(The code is invoke-expression "path\file.exe")
Details)
I am using powershell version 5
and am trying to get DSC setup to handle our sql server installations.
My manager has asked me to use the out of the box DSC components.
i.e. no downloading of custom modules which may help.
I have built up the config file that handles the base server build - everything is good.
The script resource that installs sql server is good.
It executes, and waits until it has installed completely, before moving on.
When I get up to the script resource that installs the sql server cumulative update, I have issues.
The executable gets called and it starts installing (it should take 10-15 minutes), but the dsc configuration doesn't wait until it has installed, and moves on after a second.
This means that the DependsOn for future steps, gets called, before the installation is complete.
How can I make the script resource wait until it has finished?
Have you tried the keyword "DependsOn" like that ?
Script MyNewSvc
{
GetScript = {
$SvcName = 'MyNewSvc'
$Results = #{}
$Results['svc'] = Get-Service $SvcName
$Results
}
SetScript = {
$SvcName = 'MyNewSvc'
setup.exe /param
while((Get-Service $SvcName).Status -ne "Running"){ Start-Sleep 10 }
}
TestScript = {
$SvcName = 'MyNewSvc'
$SvcLog = 'c:\svc.log'
If (condition) { #like a a running svc or a log file
$True
}
Else {
$False
}
}
}
WindowsFeature Feature
{
Name = "Web-Server"
Ensure = "Present"
DependsOn = "[Script]MyNewSvc"
}
Invoke-Expression doesn't seem to wait until the process has finished - try this in a generic PowerShell console and you'll see the command returns before you close notepad:
Invoke-Expression -Command "notepad.exe";
You can use Start-Process instead:
Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow;
And if you want to check the exit code you can do this:
$process = Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow -PassThru;
$exitcode = $process.ExitCode;
if( $exitcode -ne 0 )
{
# handle errors here
}
Finally, to use command line arguments:
$process = Start-Process -FilePath "setup.exe" -ArgumentList #("/param1", "/param2") -Wait -PassThru;
$exitcode = $process.ExitCode;

How to make the website Ping ARR server and say I am going down?

I have successfully configured the ARR in Windows Azure environment, the web server instances are added to server farm.
Using Health Check option in server farm, instance that timed-out or not responding is made unhealthy.
My Question is
Instead of the ARR web farm (doing health check every 10 seconds) ping the website, is it possible or the web role itself ping back the ARR server and say I am going down ?
Is it possible to ping the ARR Server from web role and say I am going down? or this is there any best approach available.
Please suggest.
I wanted some extra notifications with our ARR setup and I put this PowerShell script together that runs once an hour and checks the health status and notify me via email when ever any hosting server was seen as unhealthy. We also use other outside resources that ping the web farm externally once an minute and alerts us when it can't be seen (Pingdom). I have a feeling you're looking for a little more than this but I hope it helps a little.
#----- First add a reference to the MWA dll ----#
$dll=[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Web.Administration")
#----- Get the manager and config object ----#
$mgr = new-object Microsoft.Web.Administration.ServerManager
$conf = $mgr.GetApplicationHostConfiguration()
#----- Get the webFarms section ----#
$section = $conf.GetSection("webFarms")
$webFarms = $section.GetCollection()
#----- Define an array for html fragments ----#
$fragments=#()
#----- Check each webfarm ----#
foreach ($webFarm in $webFarms)
{
$Name= $webFarm.GetAttributeValue("name");
#Get the servers in the farm
$servers = $webFarm.GetCollection()
#Write-Host "Farm Name: " $Name
$fragments+= "<b>Farm Name: $Name</b>"
$fragments+="<br>"
foreach($server in $servers)
{
$ip= $server.GetAttributeValue("address")
$hostname= ([system.net.dns]::GetHostByAddress($ip)).hostname
#Get the ARR section
$arr = $server.GetChildElement("applicationRequestRouting")
$counters = $arr.GetChildElement("counters")
$isHealthy=$counters.GetAttributeValue("isHealthy")
$state= $counters.GetAttributeValue("state")
switch ($state)
{
0 {$state= "Available"}
1 {$state= "Drain"}
2 {$state= "Unavailable"}
default {$state= "Non determinato"}
}
if( $isHealthy)
{
$isHealthy="Healthy"
$fragments+="$hostname -- $ip -- $state -- $isHealthy"
$fragments+="<br>"
}
else
{
$isHealthy="Not Healthy"
$notHealthy="RED ALERT!! This is what we trained for!"
$fragments+="$hostname -- $ip -- $state -- $isHealthy"
$fragments+="<br>"
}
#Write-Host -NoNewLine $hostname " " $ip " " $state " " $isHealthy
#NEW LINE
#Write-Host
}
#NEW LINE
#Write-Host
if($notHealthy){
#write the results to HTML formated email
$smtpServer = "SMTP server"
$smtpFrom = "email address"
$smtpTo = "email address"
$messageSubject = "Unhealthy Web Server"
$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpto
$message.Subject = $messageSubject
$message.IsBodyHTML = $true
$message.Body = $fragments
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
}

Magento 1.6, Google Shopping / Products / Content

Magento 1.6 was out at the beginning of this week but upgrading from 1.5.1 with the mage_googleshopping extension (http://www.magentocommerce.com/magento-connect/Magento+Core/extension/6887/mage_googleshopping) up to v.1.6 was not something feasible.
mage_googleshopping remained compatible only with 1.5. Any working alternative to have the mage_googleshopping extension available on a clean Magento 1.6 installation or we have to wait for a stable extension release which goes with v.1.6?
Cheers,
Bogdan
I have browsed the Internet and searched for a problem to this, eventually I've turned into this script which runes separately but it connects to the Magento DB. The scripts generates the file, which can be uploaded following a schedule to Google.
Besides the fact that the script is totally editable, you can fully monitor all the processes behind it. The Magento script still can't be removed from a 1.6 Magento version.
The script wasn't developed by me but I did update it according with the latest rules enforced from 22/9/2011 by Google.
<code>
<?php
define('SAVE_FEED_LOCATION','google_base_feed.txt');
set_time_limit(1800);
require_once '../app/Mage.php';
Mage::app('default');
try{
$handle = fopen(SAVE_FEED_LOCATION, 'w');
$heading = array('id','mpn','title','description','link','image_link','price','brand','product_type','condition', 'google_product_category', 'manufacturer', 'availability');
$feed_line=implode("\t", $heading)."\r\n";
fwrite($handle, $feed_line);
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToFilter('status', 1);
$products->addAttributeToFilter('visibility', 4);
$products->addAttributeToSelect('*');
$prodIds=$products->getAllIds();
$product = Mage::getModel('catalog/product');
$counter_test = 0;
foreach($prodIds as $productId) {
if (++$counter_test < 30000){
$product->load($productId);
$product_data = array();
$product_data['sku'] = $product->getSku();
$product_data['mpn'] = $product->getSku();
$title_temp = $product->getName();
if (strlen($title_temp) > 70){
$title_temp = str_replace("Supply", "", $title_temp);
$title_temp = str_replace(" ", " ", $title_temp);
}
$product_data['title'] = $title_temp;
$product_data['description'] = substr(iconv("UTF-8","UTF-8//IGNORE",$product->getDescription()), 0, 900);
$product_data['Deeplink'] = "http://www.directmall.co.uk/".$product->getUrlPath();
$product_data['image_link'] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA).'catalog/product'.$product->getImage();
$price_temp = round($product->getPrice(),2);
$product_data['price'] = round($product->getPrice(),2) + 5;
$product_data['brand'] = $product->getData('brand');
$product_data['product_type'] = 'Laptop Chargers & Adapters';
$product_data['condition'] = "new";
$product_data['category'] = $product_data['brand'];
$product_data['manufacturer'] = $product_data['brand'];
$product_data['availability'] = "in stock";
foreach($product_data as $k=>$val){
$bad=array('"',"\r\n","\n","\r","\t");
$good=array(""," "," "," ","");
$product_data[$k] = '"'.str_replace($bad,$good,$val).'"';
}
echo $counter_test . " ";
$feed_line = implode("\t", $product_data)."\r\n";
fwrite($handle, $feed_line);
fflush($handle);
}
}
fclose($handle);
}
catch(Exception $e){
die($e->getMessage());
}</code>
Change www.directmall.co.uk into what you need.
Assign the proper permissions.
Fully updated according with Google's requirements.

How can I implement incremental (find-as-you-type) search on command line?

I'd like to write small scripts which feature incremental search (find-as-you-type) on the command line.
Use case: I have my mobile phone connected via USB, Using gammu --sendsms TEXT I can write text messages. I have the phonebook as CSV, and want to search-as-i-type on that.
What's the easiest/best way to do it? It might be in bash/zsh/Perl/Python or any other scripting language.
Edit:
Solution: Modifying Term::Complete did what I want. See below for the answer.
I get the impression GNU Readline supports this kind of thing. Though, I have not used it myself. Here is a C++ example of custom auto complete, which could easily be done in C too. There is also a Python API for readline.
This StackOverflow question gives examples in Python, one of which is ...
import readline
def completer(text, state):
options = [x in addrs where x.startswith(text)]
if state < options.length:
return options[state]
else
return None
readline.set_completer(completer)
this article on Bash autocompletion may help. This article also gives examples of programming bash's auto complete feature.
Following Aiden Bell's hint, I tried Readline in Perl.
Solution 1 using Term::Complete (also used by CPAN, I think):
use Term::Complete;
my $F;
open($F,"<","bin/phonebook.csv");
my #terms = <$F>; chomp(#terms);
close($F);
my $input;
while (!defined $input) {
$input = Complete("Enter a name or number: ",#terms);
my ($name,$number) = split(/\t/,$input);
print("Sending SMS to $name ($number).\n");
system("sudo gammu --sendsms TEXT $number");
}
Press \ to complete, press Ctrl-D to see all possibilities.
Solution 2: Ctrl-D is one keystroke to much, so using standard Term::Readline allows completion and the display off possible completions using only \.
use Term::ReadLine;
my $F;
open($F,"<","bin/phonebook.csv");
my #terms = <$F>; chomp(#terms);
close($F);
my $term = new Term::ReadLine;
$term->Attribs->{completion_function} = sub { return #terms; };
my $prompt = "Enter name or number >> ";
my $OUT = $term->OUT || \*STDOUT;
while ( defined (my $input = $term->readline($prompt)) ) {
my ($name,$number) = split(/\t/,$input);
print("Sending SMS to $name ($number).\n");
system("sudo gammu --sendsms TEXT $number");
}
This solution still needs a for completion.
Edit: Final Solution
Modifying Term::Complete (http://search.cpan.org/~jesse/perl-5.12.0/lib/Term/Complete.pm) does give me on the fly completion.
Source code: http://search.cpan.org/CPAN/authors/id/J/JE/JESSE/perl-5.12.0.tar.gz
Solution number 1 works with this modification. I will put the whole sample online somewhere else if this can be used by somebody
Modifications of Completion.pm (just reusing it's code for Control-D and \ for each character):
170c172,189
my $redo=0;
#match = grep(/^\Q$return/, #cmp_lst);
unless ($#match < 0) {
$l = length($test = shift(#match));
foreach $cmp (#match) {
until (substr($cmp, 0, $l) eq substr($test, 0, $l)) {
$l--;
}
}
print("\a");
print($test = substr($test, $r, $l - $r));
$redo = $l - $r == 0;
if ($redo) { print(join("\r\n", '', grep(/^\Q$return/, #cmp_lst)), "\r\n"); }
$r = length($return .= $test);
}
if ($redo) { redo LOOP; } else { last CASE; }