Ansible: ios upgrade router: check "spacefree_kb" prior to image copy - regex

I'm writing a playbook for ios upgrade of multiple switches and have most pieces working with exception of the flash free check. Basically, I want to check if there is enough flash space free prior to copying the image.
I tried using the gather facts module but it is not working how I expected:
from gather facts I see this:
"ansible_net_filesystems_info": {
"flash:": {
"spacefree_kb": 37492,
"spacetotal_kb": 56574
This is the check I want to do:
fail:
msg: 'This device does not have enough flash memory to proceed.'
when: "ansible_net_filesystems_info | json_query('*.spacefree_kb')|int < new_ios_filesize|int"
From doing some research I understand that any value returned by a jinja2 template will be a string so my check is failing:
Pass integer variable to task without losing the integer type
The solution suggested in the link doesn't seem to work for me even with ansible 2.7.
I then resorted to store the results of 'dir' in a register and tried using regex_search but can't seem to get the syntax right.
(similar to this :
Ansible regex_findall multiple strings)
"stdout_lines": [
[
"Directory of flash:/",
"",
" 2 -rwx 785 Jul 2 2019 15:39:05 +00:00 dhcp-snooping.db",
" 3 -rwx 1944 Jul 28 2018 20:05:20 +00:00 vlan.dat",
" 4 -rwx 3096 Jul 2 2019 01:03:26 +00:00 multiple-fs",
" 5 -rwx 1915 Jul 2 2019 01:03:26 +00:00 private-config.text",
" 7 -rwx 35800 Jul 2 2019 01:03:25 +00:00 config.text",
" 8 drwx 512 Apr 25 2015 00:03:16 +00:00 c2960s-universalk9-mz.150-2.SE7",
" 622 drwx 512 Apr 25 2015 00:03:17 +00:00 dc_profile_dir",
"",
"57931776 bytes total (38391808 bytes free)"
]
]
Can anyone provide some insight to this seemingly simple task? I just want '38391808' as an integer from the example above (or any other suggestion). I'm fairly new to ansible.
Thanks in advance.

json_query wildcard expressions return a list. The tasks below
- set_fact:
free_space: "{{ ansible_net_filesystems_info|
json_query('*.spacefree_kb') }}"
- debug:
var: free_space
give the list
"free_space": [
37492
]
which neither can be converted to an integer nor can be compared to an integer. This is the reason for the problem.
The solution is simple. Just take the first element of the list and the condition will start working
- fail:
msg: 'This device does not have enough flash memory to proceed.'
when: ansible_net_filesystems_info|
json_query('*.spacefree_kb')|
first|
int < new_ios_filesize|int
Moreover, json_query is not necessary. The attribute spacefree_kb can be referenced directly
- fail:
msg: 'This device does not have enough flash memory to proceed.'
when: ansible_net_filesystems_info['flash:'].spacefree_kb|
int < new_ios_filesize|int

json_query has an advantage : see this example on a C9500 :
[{'bootflash:': {'spacetotal_kb': 10986424.0, 'spacefree_kb': 4391116.0}}]
yes they changed flash: to bootflash:.

Related

How to restrict matches to the first 5 lines of an email body using regex in GAS [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm using the following script which is working correctly to pull 2 fields out of an email body.
This is causing the script execution time to increase significantly due to the amount of content in the body. Is there a way to make this search through only the first 5 lines of the email body?
First lines of e-mail:
Name: Full Report
Store: River North (Wells St)
Date Tripped: 19 Feb 2020 1:07 PM
Business Date: 19 Feb 2020 (Open)
Message:
Information:
This alert was tripped based on a user defined trigger: Every 15 minutes.
Script:
//gets first(latest) message with set label
var threads = GmailApp.getUserLabelByName('South Loop').getThreads(0,1);
if (threads && threads.length > 0) {
var message = threads[0].getMessages()[0];
// Get the first email message of a threads
var tmp,
subject = message.getSubject(),
content = message.getPlainBody();
// Get the plain text body of the email message
// You may also use getRawContent() for parsing HTML
// Implement Parsing rules using regular expressions
if (content) {
tmp = content.match(/Date Tripped:\s*([:\w\s]+)\r?\n/);
var tripped = (tmp && tmp[1]) ? tmp[1].trim() : 'N/A';
tmp = content.match(/Business Date:\s([\w\s]+\(\w+\))/);
var businessdate = (tmp && tmp[1]) ? tmp[1].trim() : 'N/A';
}
}
You can use the pattern /^(?:.*\r?\n){0,5}/ to grab the first 5 lines of the email, then run your search against this smaller string. Here's a browser example with hardcoded content, but I tested it in Google Apps Script.
const Logger = console; // Remove this for GAS!
const content = `Name: Full Report
Store: River North (Wells St)
Date Tripped: 19 Feb 2020 1:07 PM
Business Date: 19 Feb 2020 (Open)
Message:
Information:
This alert was tripped based on a user defined trigger: Every 15 minutes.`;
const searchPattern = /(Date Tripped|Business Date): *(.+?)\r?\n/g;
const matches = [...content.match(/^(?:.*\r?\n){0,5}/)[0]
.matchAll(searchPattern)]
const result = Object.fromEntries(matches.map(e => e.slice(1)));
Logger.log(result);
If you wish to dynamically inject the search terms, use:
const Logger = console; // Remove this for GAS!
const content = `Name: Full Report
Store: River North (Wells St)
Date Tripped: 19 Feb 2020 1:07 PM
Business Date: 19 Feb 2020 (Open)
Foo: this will match because it's on line 5
Bar: this won't match because it's on line 6
Information:
`;
const searchTerms = ["Date Tripped", "Business Date", "Foo", "Bar"];
const searchPattern = new RegExp(`(${searchTerms.join("|")}): *(.+?)\r?\n`, "g");
const matches = [...content.match(/^(?:.*\r?\n){0,5}/)[0]
.matchAll(searchPattern)]
const result = Object.fromEntries(matches.map(e => e.slice(1)));
Logger.log(result);
ES5 version if you're using the older engine:
var Logger = console; // Remove this for GAS!
var content = "Name: Full Report\nStore: River North (Wells St)\nDate Tripped: 19 Feb 2020 1:07 PM\nBusiness Date: 19 Feb 2020 (Open)\nMessage:\nInformation:\nThis alert was tripped based on a user defined trigger: Every 15 minutes.\n";
var searchPattern = /(Date Tripped|Business Date): *(.+?)\r?\n/g;
var truncatedContent = content.match(/^(?:.*\r?\n){0,5}/)[0];
var result = {};
for (var m; m = searchPattern.exec(content); result[m[1]] = m[2]);
Logger.log(result);
#ggorlen's answer is not precise, to my taste. Let's have a look at regex01
My problem with (?:.*\r?\n){0,5} is this: in english this regex says:
Take any number of characters (0 or more) ending with a newline.
Do this between 0 and 5 times.
Which means any empty string matches. If you would do a global match, there's a lot of those.
So, how could you grab the first 5 lines? Be exact! So something like
^([^\r\n]+\r?\n){5}
See regex101
P.S. #ggorlen mentioned I left the default multiline matching on in regex101, and he's right about that. Your preference may vary: choosing between ignoring messages with less than 5 lines and accepting strings with empty lines depends on your particular case.
P.S.2 I've adapted my wording and disabled the multiline and global settings in regex101 to display my concerns with it.

"\S+?#\S+" and "\S+#\S+" give same output in regular expressions

The following two regularexpressions give the same output in python 3.7.
"+?" is supposed to be non-greedy
re.findall("\S+?#\S+?","From stephen.marquard#uct.ac.za Sat Jan 5 09:14:16 2008")
re.findall("\S+#\S+","From stephen.marquard#uct.ac.za Sat Jan 5 09:14:16 2008")
Both of these give the same output as:
['stephen.marquard#uct.ac.za']
The +? is non-greedy as expected. Refer https://regex101.com/r/DM4voj/1
If you copy pasted both the commands into your shell or program, you will only get the output of the last command. Try using print statements for both. You should get the desired answers.
import re
print(re.findall("\S+?#\S+?","From stephen.marquard#uct.ac.za Sat Jan 5 09:14:16 2008"))
print(re.findall("\S+#\S+","From stephen.marquard#uct.ac.za Sat Jan 5 09:14:16 2008"))
The result will be as below as expected
['stephen.marquard#u']
['stephen.marquard#uct.ac.za']

Groovy String replacement with link

I have multi-lines string from git log in variable
and want to replace matched lines with hyper-links
but keep some parts of the original string with Groovy.
Example:
commit 7a1825abc69f1b40fd8eb3b501813f21e09bfb54
Author: Filip Stefanov
Date: Mon Nov 21 11:05:08 2016 +0200
TICKET-1
Test change
Change-Id: I7b4028e504de6c4a48fc34635d4b94ad038811a6
Should look like:
commit 7a1825abc69f1b40fd8eb3b501813f21e09bfb54
Author: Filip Stefanov
Date: Mon Nov 21 11:05:08 2016 +0200
<a href=http://localhost:8080/browse/TICKET-1>TICKET-1</a>
Test change
<a href=http://localhost:8081/#/q/I7b4028e504de6c4a48fc34635d4b94ad038811a6,n,z>Change-Id: I7b4028e504de6c4a48fc34635d4b94ad038811a6</a>
Im pretty bad in Groovy regex dont know how to use grouping or closures so far so good:
mystring.replaceAll(/TICKET-/, "http://localhost:8080/browse/TICKET-")
NOTE:
TICKET {int} and Change-Id {hash} are variables
mystring.replaceAll(/(TICKET-\d++)/, '$1')
.replaceAll(/Change-Id: (I\p{XDigit}++)/, 'Change-Id: $1')
Of course you have to replace the dynamic parts accordingly. Currently it is at least one digit after the TICKET- and an I and then at least one hex digit after the Change-ID:.

Regular expression to debatch MT940 message

I got a message with below structure, where message starts from tag :20: and ends at :86:. I want to write a regular expression to extract the all messages.
I would write a C# utility to extract each message and put it in ArrayList.
:20:160212-2359
:21:600******444
:28C:00001/00001
.
.
.
:86:DAILY SETTLEMENT /ENTRY-13 MAR
:62F:D160212GBP1229387,45
:64:D160212GBP1229387,45
:65:D120314GBP1229387,45
:65:D120315GBP1229387,45
:65:D120316GBP1229387,45
:65:D120317GBP1229387,45
:65:D120318GBP1229387,45
:86:FORWARD AVAILABLE FUNDS SHOW ITEMS KNOWN BUT NOT YET POSTED
some more comments in 86_2 segment
this is line2
:20:160212-2359
:21:B***22
:25:60*****88
.
.
.
:86:/ENTRY-13 MAR TRF/REF 6*******64 /ORD/ some line here
*********************** /BNF/ JO 88
:62F:C160212EUR13868931,00
:64:C160212EUR13868931,00
:65:C120314EUR13868931,00
:65:C120315EUR13791849,00
:65:C120316EUR13791849,00
:65:C120317EUR13791849,00
:65:C120318EUR13791849,00
:86:FORWARD AVAILABLE FUNDS SHOW ITEMS KNOWN BUT NOT YET POSTED
some more comments in 86_2 segment.
:20:160212-2359
:21:B****X
:25:6*************1
:28C:00001/00001
:86:STORE1 EUROPE B.V. /ENTRY-15 MAR RTS/REF 6*****6 RTS
SWEPT FROM 9999 1**** XX***********BILLING CHARGES -
28FEB12 TRF/REF 6641XXX43799053 /ITEMCNT/004 /BNF/ /ITEMCNT/004
BILLING CHARGES
:61:1203130313DR10000000,00****288//6*****6
:86:STORE1 CNRTY SRL /ENTRY-13 MAR CLG/REF 66**********6
:61:1*****000,00NT*****9846//6******74
:86:NAME /ENTRY-13 MAR CLG/REF 6******4 LA C****R
**** CASH DEPOSIT STORE1
:61:1203150315DR48531,00NCHGBILLING CHARGES//6641XXX43799053
:86:BILLING CHARGES - 28FEB12 /ENTRY-15 MAR TRF/REF
66******53 /ITEMCNT/004
:62F:C160212EUR0,00
:64:C160212EUR0,00
:65:C120314EUR0,00
:65:C120315EUR0,00
:65:C120316EUR0,00
:65:C120317EUR0,00
:65:C120318EUR0,00
:86:FORWARD AVAILABLE FUNDS SHOW ITEMS KNOWN BUT NOT YET POSTED
{newline}
Actual values are replaced with '*' character.
Thanks
Dhiraj Bhavsar
Try this
:20:(.*?):86:
in code
/:20:(.*?):86:/gs
https://regex101.com/r/dW4zS3/1
.*? matches any character between zero and unlimited times, as few times as possible, expanding as needed

Perl: Deleting multiple reccuring lines where a certain criterion is met

I have data that looks like below, the actual file is thousands of lines long.
Event_time Cease_time
Object_of_reference
-------------------------- --------------------------
----------------------------------------------------------------------------------
Apr 5 2010 5:54PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSJN1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=LUGALAMBO_900
Apr 5 2010 5:55PM Apr 5 2010 6:43PM
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSJN1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=LUGALAMBO_900
Apr 5 2010 5:58PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
Apr 5 2010 5:58PM Apr 5 2010 6:01PM
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
Apr 5 2010 6:01PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
Apr 5 2010 6:03PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSJN1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=KAPKWAI_900
Apr 5 2010 6:03PM Apr 5 2010 6:04PM
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSJN1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=KAPKWAI_900
Apr 5 2010 6:04PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSJN1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=KAPKWAI_900
Apr 5 2010 6:03PM Apr 5 2010 6:03PM
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
Apr 5 2010 6:03PM NULL
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
Apr 5 2010 6:03PM Apr 5 2010 7:01PM
SubNetwork=ONRM_RootMo,SubNetwork=AXE,ManagedElement=BSCC1,BssFunction=
BSS_ManagedFunction,BtsSiteMgr=BULAGA
As you can see, each file has a header which describes what the various fields stand for(event start time, event cease time, affected element). The header is followed by a number of dashes.
My issue is that, in the data, you see a number of entries where the cease time is NULL i.e event is still active. All such entries must go i.e for each element where the alarm cease time is NULL, the start time, the cease time(in this case NULL) and the actual element must be deleted from the file.
In the remaining data, all the text starting from word SubNetwork upto BtsSiteMgr= must also go. Along with the headers and the dashes.
Final output should look like below:
Apr 5 2010 5:55PM Apr 5 2010 6:43PM
LUGALAMBO_900
Apr 5 2010 5:58PM Apr 5 2010 6:01PM
BULAGA
Apr 5 2010 6:03PM Apr 5 2010 6:04PM
KAPKWAI_900
Apr 5 2010 6:03PM Apr 5 2010 6:03PM
BULAGA
Apr 5 2010 6:03PM Apr 5 2010 7:01PM
BULAGA
Below is a Perl script that I have written. It has taken care of the headers, the dashes, the NULL entries but I have failed to delete the lines following the NULL entries so as to produce the above output.
#!/usr/bin/perl
use strict;
use warnings;
$^I=".bak" #Backup the file before messing it up.
open (DATAIN,"<george_perl.txt")|| die("can't open datafile: $!"); # Read in the data
open (DATAOUT,">gen_results.txt")|| die("can't open datafile: $!"); #Prepare for the writing
while (<DATAIN>) {
s/Event_time//g;
s/Cease_time//g;
s/Object_of_reference//g;
s/\-//g; #Preceding 4 statements are for cleaning out the headers
my $theline=$_;
if ($theline =~ /NULL/){
next;
next if $theline =~ /SubN/;
}
else{
print DATAOUT $theline;
}
}
close DATAIN;
close DATAOUT;
Kindly help point out any modifications I need to make on the script to make it produce the necessary output.
Your data arrives in sets of 3 lines, so one approach is to organize the parsing that way:
use strict;
use warnings;
# Ignore header junk.
while (<>){
last unless /\S/;
}
until (eof) {
# Read in a set of 3 lines.
my #lines;
push #lines, scalar <> for 1 .. 3;
# Filter and clean.
next if $lines[0] =~ /\sNULL\s/;
$lines[2] =~ s/.+BtsSiteMgr=//;
print #lines[0,2];
}
Looks like a good candidate for a little input record separator ($/) trickery. The idea is to manipulate it so that it deals with one record at a time, rather than the default single line.
use strict;
use warnings;
$^I = '.bak';
open my $dataIn, '<', 'george_perl.txt' or die "Can't open data file: $!";
open my $dataOut, '>', 'gen_results.txt' or die "Can't open output file: $!";
{
local $/ = "\n\t"; # Records have leading tabs
while ( my $record = <$dataIn> ) {
# Skip header & records that contain 'NULL'
next if $record =~ /NULL|Event_time/;
# Strip out the unwanted yik-yak
$record =~ s/SubNetwork.*BtsSiteMgr=//s;
# Print record to output file
print $dataOut $record;
}
}
close $dataIn;
close $dataOut;
Pay attention to the following:
use of the safer three-argument form of open (the two-argument form is what you've shown)
use of scalar variables rather than barewords for defining filehandles
use of the local keyword and extra curlies to modify the definition of $/ only when needed.
the second s in s/SubNetwork.*BtsSitMgr=//s allows matches over multiple lines as well.
s/^.*NULL\r?\n.*\r?\n.*\r?\n//mg;
should filter out the lines that end in NULL plus the two following lines.