How to insert lines after second pattern with linux sed command - linux

I want to insert this block:
host client3 {
hardware ethernet c0:03:03:bc:30:fa;
}
after this block:
subnet 11.10.0.0 netmask 255.255.255.0 {
range 11.10.1.2 11.10.1.254;
group {
filename "10M-5M-OKS2016NOV.cm";
The line: filename "10M-5M-OKS2016NOV.cm";
apears multiple times in the file. But only once inside subnet 11.10.0.0 netmask 255.255.255.0 {
Until now I can print the subnet block until the "filename":
sed -n -e :a -e '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/,/}/{/filename "10M-5M-OKS2016NOV\.cm";/!{$!{N;ba};};p;}' dhcpd.conf
but when I try:
sed -n -e :a -e '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/,/}/{/filename "10M-5M-OKS2016NOV\.cm";/!{$!{N;ba};};a\ \thost client3 {\n\thardware ethernet c0:03:03:bc:30:fa;\n\t}\n;}' dhcpd.conf
I get:
sed: -e expression #1, char 0: unmatched `{'
subnet 10.10.0.0 netmask 255.255.255.0 {
range 10.10.0.2 10.10.0.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client1 {
hardware ethernet a0:b4:3d:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:bb:ba:cd:d4;
}
}
}
subnet 11.10.0.0 netmask 255.255.255.0 {
range 11.10.1.2 11.10.1.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client1 {
hardware ethernet c0:14:e3:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:fb:ba:3d:04;
}
}
}
subnet 12.10.0.0 netmask 255.255.255.0 {
range 12.10.2.2 12.10.2.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client1 {
hardware ethernet c0:a4:3d:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:bb:ca:3d:04;
}
}
}

Please try something like:
#!/bin/bash
# define newline and tab characters for replacement
NL=$'\n'
NL="\\$NL"
TAB=$'\t'
TAB="\\$TAB"
sed '
:l
N
$!b l
# first of all slurp all lines in the pattern space
# and perform the replacement over the lines
s/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0[^}]*filename "10M-5M-OKS2016NOV\.cm";/&'"$NL$TAB"'host client3 {'"$NL$TAB$TAB"'hardware ethernet c0:03:03:bc:30:fa;'"$NL$TAB"'}/g
' dhcpd.conf
It yields the following output by using the posted lines as dhcpd.conf,
subnet 10.10.0.0 netmask 255.255.255.0 {
range 10.10.0.2 10.10.0.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client1 {
hardware ethernet a0:b4:3d:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:bb:ba:cd:d4;
}
}
}
subnet 11.10.0.0 netmask 255.255.255.0 {
range 11.10.1.2 11.10.1.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client3 {
hardware ethernet c0:03:03:bc:30:fa;
}
host client1 {
hardware ethernet c0:14:e3:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:fb:ba:3d:04;
}
}
}
subnet 12.10.0.0 netmask 255.255.255.0 {
range 12.10.2.2 12.10.2.254;
group {
filename "10M-5M-OKS2016NOV.cm";
host client1 {
hardware ethernet c0:a4:3d:bc:df:fa;
}
host client2 {
hardware ethernet 90:6e:bb:ca:3d:04;
}
}
}
It initially slurps all the lines at first to process multi lines efficiently.
It assumes the right curly brace } does not appear in the search target block
to achieve the shortest match in regex.
Hope this helps.

This might work for you (GNU sed):
sed '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/{:a;n;/filename "10M-5M-OKS2016NOV\.cm";/!ba;p;s/\S.*/host client3 {/p;s// hardware ethernet c0:03:03:bc:30:fa;/p;s//}/}' file
This finds the first line containing subnet 11.10.0.0 netmask 255.255.255.0 and then reads on further until the line containing filename "10M-5M-OKS2016NOV.cm";. After printing that line it uses the line as a template to format the required detail.
Another solution, using a preformed insertion file:
cat <<\! | sed '/subnet 11\.10\.0\.0 netmask 255\.255\.255\.0/!b;:a;n;/filename "10M-5M-OKS2016NOV\.cm";/!ba;r /dev/stdin' file
host client3 {
hardware ethernet c0:03:03:bc:30:fa;
}
!

sed is great as a stream editor, that means to process multiple times the same actions. Here you just want to insert once a bloc of text. That would be much simpler (more readable and maintenable) with ed:
ed dhcpd.conf <<EOF
/subnet 11.10.0.0/
/filename/
a
host client3 {
hardware ethernet c0:03:03:bc:30:fa;
}
.
w
q
EOF
Beware: ed is a file editor. That means that the dhcpd.conf file will be changed by the above script. Make sure to have a backup if things goes wrong...

Related

DRBD Parse error: got 'incon-degr-cmd' (TK 282) on CentOS

Setup
I currently have two NFS servers. And the plan is that they mirror their data to each other in realtime using DRBD and monitor each other using heartbeat.
This is my current /etc/drbd.d/t0.res config.
resource t0 {
protocol C;
incon-degr-cmd "halt -f";
startup {
degr-wfc-timeout 120; # 2 minutes.
}
disk {
on-io-error detach;
}
net {
}
syncer {
rate 10M;
group 1;
al-extents 257;
}
on node1 {
device /dev/drbd0;
disk /dev/loop0;
address 172.16.2.101:7788;
meta-disk internal;
}
on node2 {
device /dev/drbd0;
disk /dev/loop0;
address 172.16.2.102:7788;
meta-disk internal;
}
}
Error
When I try to use a drbdadm command I get the following error:
drbd.d/contentserver.res:4: Parse error: 'protocol | on | disk | net | syncer | startup | handlers | ignore-on | stacked-on-top-of' expected,
but got 'incon-degr-cmd' (TK 282)
I believe your resource file should read like this:
resource t0 {
protocol C;
pri-on-incon-degr "halt -f";
startup {
degr-wfc-timeout 120; # 2 minutes.
}
disk {
on-io-error detach;
}
net {
}
syncer {
rate 10M;
group 1;
al-extents 257;
}
on node1 {
device /dev/drbd0;
disk /dev/loop0;
address 172.16.2.101:7788;
meta-disk internal;
}
on node2 {
device /dev/drbd0;
disk /dev/loop0;
address 172.16.2.102:7788;
meta-disk internal;
}
}

Replace a string from a set of string in a file - SED

I am having a trouble replacing string coming from a set of string from a File
So here's how I would like my script to run
I have these files:
macadd.file
00:90:8F:56:63:88
00:90:8F:56:63:77
00:90:8F:56:63:6B
00:90:8F:56:63:86
00:90:8F:56:63:87
00:90:8F:56:64:E5
test.file
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
So I've tried replacing it using
for i in $(cat macadd); do sed -i "s/00:90:8F:56:63:88/$i/g" test;done
However, it only changes it from the first string in macadd.file
You can see that 00:90:8F:56:63:88 is all the entry there. What I would like to happen is like this, the result:
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:77; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:6B; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:86; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:87; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:64:E5; fixed-address 192.168.10.29; }
Actually for the example you present in the question something without the substitution would be more simple and clear:
<macadd.file xargs -I{} printf "host Accountant { hardware ethernet %s; fixed-address 192.168.10.29; }\n" {}
That is for every line in the input file call printf to substitute only the MAC address part. More trouble comes when you have more MAC addresses and more resulting IP addresses.
The first pass across causes sed to replace all the hits with the first value. If that's not what you want, use a different sed script. Perhaps like this:
sed 's!.*!s/00:90:8F:56:63:88/&/;n;!' macadd.file
This produces on standard output a new sed script, which you can pass to (... pause ...) sed.
sed 's!.*!s/00:90:8F:56:63:88/&/;n;' macadd.file |
sed -f - -i test.file
This is a slightly demanding pattern the first time you see it (and sed -f - does not work on all platforms) but once you get the idea, having a script generate a script is a very powerful and versatile tool for your toolbox.
... On MacOS, you can't use sed -f - and also -i requires an argument. If you have Bash (or some other shell which supports process substitution), here's a workaround:
sed -f <(sed 's!.*!s/00:90:8F:56:63:88/&/;n;!' macadd.file) -i '' test.file
Straightforwardly with AWK:
awk 'NR==FNR{ a[NR]=$1; next }
a[FNR]{ sub("00:90:8F:56:63:88", a[FNR]) }1' macadd tesfile > tmp_f && mv tmp_f testfile
NR - the total number of input records read so far
FNR - the number of records that have been read so far from the current input file
The final testfile contents:
host Accountant { hardware ethernet 00:90:8F:56:63:88; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:77; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:6B; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:86; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:63:87; fixed-address 192.168.10.29; }
host Accountant { hardware ethernet 00:90:8F:56:64:E5; fixed-address 192.168.10.29; }
If MAC adresses are not always the same in test.file
You can try :
sed -E '
s/((([[:xdigit:]]){2}:){5}[[:xdigit:]]{2})(.*)((([[:xdigit:]]){2}:){5}[[:xdigit:]]{2})(.*)/\4\1\8/
# ((([[:xdigit:]]){2}:){5}[[:xdigit:]]{2}) catch the MAC addresse
s/^\t//
# remove tab which come from paste
/^((([[:xdigit:]]){2}:){5}[[:xdigit:]]{2})/d
# remove lines if maccad.file have more lines than test.file
'<<< $(paste macadd.file test.file)>test.file

Server can't find: SERVFAIL or REFUSE DNS

I'm trying to setup a DNS Server using Debian but I keep getting errors when I do nslookup like SERVFAIL or REFUSED.
I want to use 3 virtual machines (VM1, VM2 and VM3) and call them that by those names in the DNS Server, I'm using VMWare Workstation 11.
Here is my configuration:
named.conf.options
options {
directory "/var/cache/bind";
additional-from-auth no;
additional-from-cache no;
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.
forwarders {
192.168.207.2;
192.168.207.133;
};
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation yes;
allow-recursion{127.0.0.1;};
auth-nxdomain no; # conform to RFC1035
listen-on-v6 { any; };
};
named.conf.local
zone "linux.local"{
type master;
file "etc/bind/db.linux.local";
};
zone "207.168.192-in-addr.arpa"{
type master;
file "etc/bind/db.207.168.192";
};
db.linux.local
;
; SOA
;
$TTL 1h
# IN SOA vm1.linux.local. root.linux.local. (
1 ; Serial number (YYYYMMDDnn)
1h ; Slave refresh
15m ; Slave retry
2w ; Slave expire
1h ; Cache TTL
)
;
; NS RECORDS
;
# IN NS vm1.linux.local.
;
; A RECORDS
;
linux.local. IN A 192.168.207.133
# IN A 192.168.207.133
vm1 IN A 192.168.207.133
vm3 IN A 192.168.207.135
vm2 IN A 192.168.207.130
vmware iN A 192.168.207.2
db.207.168.192
$TTL 1h
# IN SOA vm1.linux.local. root.linux.local. (
1;
1h;
15m;
2w;
1h;
)
IN NS vm1.linux.local.
133 IN PTR linux.local.
133 IN PTR vm1.linux.local.
135 IN PTR vm2.linux.local.
130 IN PTR vm3.linux.local.
2 IN PTR vmware.linux.local.
Here is the nslookup for VM1 and linux.local:
root#debian:/etc/bind# nslookup vm1
Server: 192.168.207.133
Address: 192.168.207.133#53
** server can't find vm1: REFUSED
root#debian:/etc/bind# nslookup linux.local
Server: 192.168.207.133
Address: 192.168.207.133#53
** server can't find linux.local.linux.local: SERVFAIL
maybe the access is limited. try edit the file named.conf, change or add options allow-query { any;};
run
rndc-confgen >> /etc/named.conf
This should fix the issue.

Given an array of hostnames, how can I generate a set of files based on those hostnames in puppet

I am not sure there is a way to even do this in puppet, but here is what I am trying to do.
Given this skeletal puppet class definition ...
class make_files (
$rabbit_servers = ['rabbit-1','rabbit-2'],
$mongo_servers = ['mongo-1','mongo-2'],
) {
...
}
... generate the files ...
# pwd
/root/conf/hosts
# more rabbit-*
::::::::::::::
rabbit-1.cfg
::::::::::::::
define host {
use linux-server
host_name rabbit-1
alias Rabbit MQ host
hostgroups rabbit_hosts
address 10.29.103.33
}
::::::::::::::
rabbit-2.cfg
::::::::::::::
define host {
use linux-server
host_name rabbit-2
alias Rabbit MQ host
hostgroups rabbit_hosts
address 10.29.103.34
}
# more mongo-*
::::::::::::::
mongo-1.cfg
::::::::::::::
define host {
use linux-server
host_name mongo-1
alias Mongo DB host
hostgroups mongo_hosts
address 10.29.103.31
}
::::::::::::::
mongo-2.cfg
::::::::::::::
define host {
use linux-server
host_name mongo-2
alias Mongo DB host
hostgroups mongo_hosts
address 10.29.103.32
}
Where the IP addresses are the IP address of the corresponding host.
Any help is much appreciated.
If the names belong to nodes that are managed by Puppet, you can fetch the addresses from PuppetDB directly using the puppetdbquery module
$ipaddress = query_facts("hostname=$host_name", ['ipaddress'])['ipaddress']
The code is untested, I'm not sure whether the invocation is correct.
I have found a solution ...
class make_files (
$rabbit_servers = ['rabbit-1:10.29.103.33','rabbit-2:10.29.103.34'],
$mongo_servers = ['ost-mongo-el7-001:10.29.103.31'],
) {
define my_file ($content, $hostgroup) {
$tuple = split($name, ':')
$host_name = $tuple[0]
$file_name = "/tmp/$host_name.cfg"
$ipaddress = $tuple[1]
$config = "define host {
use linux-server
host_name $host_name
alias $host_name
hostgroups $hostgroup
address $ipaddress
}"
file { $file_name:
ensure => file,
content => $config,
}
}
$rabbit_content = join($rabbit_servers, ',')
my_file { $rabbit_servers: content => $rabbit_content, hostgroup => 'rabbit_hosts' }
$mongo_content = join($mongo_servers, ',')
my_file { $mongo_servers: content => $mongo_content, hostgroup => 'mongo_hosts' }
}
... and I found that I needed to change the content of the files slightly.
This is probably not the best answer but it seems to work. I am open to suggested improvements.
Thanks

Linux kernel cannot receive multicast

I built a Linux kernel with CONFIG_IP_MULTICAST=y,however no UDP multicast package received in this kernel while UDP unicast works well.
ethtool -S eth0 | grep multicast
txmulticastframes_g: 0
txmulticastframes_gb: 0
rxmulticastframes_g: 0
Any hints how can I solve this problem?
Thx. Forrest G
=================================================================================
Additional:
tcpdump can get the packet
root#JHI # ./tcpdump port 3702
device eth0 entered promiscuous mode
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
-7:-48:-19.4151 IP 192.168.42.212.3702 > 239.255.255.250.3702: UDP, length 787
-7:-48:-19.4661 IP 192.168.42.212.3702 > 239.255.255.250.3702: UDP, length 803
^C
2 packets captured
2 padevice eth0 left promiscuous mode
ckets received by filter
0 packets dropped by kernel
I write this WS-devicediscovery function with gSOAP. It works on X86 machine. When running on ARM device, it can send out igmp packet but not receive anything.
void wsdd()
{
struct soap *soap_udp;
struct ip_mreq mreq;
soap_udp=soap_new();
soap_init1(soap_udp, SOAP_IO_UDP|SOAP_IO_FLUSH);
if (!soap_valid_socket(soap_bind(soap_udp, NULL, 3702, 100)))
{
soap_print_fault(soap_udp, stderr);
}
mreq.imr_multiaddr.s_addr = inet_addr("239.255.255.250");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if(setsockopt(soap_ud->master,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))==-1) {
perror("membership error\n");
}
int loop = 1;
int sock_opt = 1;
if ((setsockopt(soap_udp->master, SOL_SOCKET, SO_REUSEADDR, (void *) &sock_opt,
sizeof (sock_opt))) == -1) {
printf("setsockopt\n");
}
if ((setsockopt(soap_udp->master, IPPROTO_IP, IP_MULTICAST_LOOP,
&loop, sizeof (loop))) == -1) {
printf("setsockopt\n");
}
while(1){
soap_accept(soap_udp);
soap_serve(soap_udp);
soap_end(soap_udp);
}
}
Then I have tried these things but still not work
route add -net 224.0.0.0 netmask 240.0.0.0 eth0
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
Problem solved by changing..
ifconfig eth0 promisc
Can anyone explain the principle?

Resources