How to replace words/digits in a file - python

I'm trying to replace the global IP inside a file with a private one and create a mapping of them, so i can revert it back even if part of the new string is different.
I'm stuck at the point of replace the global IP with the bogus one and write it to a file.
Starting file example:
ip route 192.168.1.0 255.255.0.0 10.10.10.2
ip route 192.168.1.0 255.255.0.0 1.1.1.2
ip route 1.1.1.1 255.255.0.0 1.1.1.3
interface FastEthernet1
ip address 1.1.1.1
duplex auto
speed auto
Wanted end result, some wording may change before revert back:
ip route ipv4 192.168.1.0 255.255.0.0 10.10.10.2
ip route ipv4 192.168.1.0 255.255.0.0 10.1.1.11
ip route ipv4 10.1.1.10 255.255.0.0 10.1.1.12
interface FastEthernet1
ip address 10.1.1.10
duplex auto
speed auto
The mapping I though is a dictionary like this:
mapping = {
'1.1.1.2': "10.1.1.10",
'1.1.1.1': "10.1.1.10",
'1.1.1.3': "10.1.1.30
}
I came out with this script until now, but it not doing what I want:
import re
import ipaddress
def load_file(file) -> str:
with open(file, 'r') as f:
return f.read()
def find_ips(config) -> set:
ip_regex = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'
match = set(re.findall(ip_regex, config))
return match
def mapping_ip(ips) -> dict:
counter = 0
ip_table = {}
for ip in ips:
ip4_type = ipaddress.IPv4Address(ip)
if ip4_type.is_global:
counter += 1
private = ipaddress.IPv4Address('10.1.1.10') + counter
ip_table.update({
ip: str(private),
})
return ip_table
def replace(mapping, s_file, d_file):
with open(s_file, 'r') as reader, open(d_file, 'w') as writer:
for line in reader:
for orig, temp in mapping.items():
if orig in line:
x = line.replace(orig, temp)
writer.write(x)
Any suggestion on how should I do the replace funcion?
Only the IP can be change, the rest of the string need to stay as it's(revert back process).

You can simply use string replace on the lines of your source file:
Create source file:
t = """ip route 192.168.1.0 255.255.0.0 10.10.10.2
ip route 192.168.1.0 255.255.0.0 1.1.1.2
ip route 1.1.1.1 255.255.0.0 1.1.1.3
interface FastEthernet1
ip address 1.1.1.1
duplex auto
speed auto"""
with open("t.txt","w") as f:
f.write(t)
Replace stuff and write to "mod.txt":
mapping = {
'1.1.1.2': "10.1.1.10",
'1.1.1.1': "10.1.1.10",
'1.1.1.3': "10.1.1.30"
}
with open("mod.txt","w") as m, open("t.txt") as data:
for line in data:
for key,replacewith in mapping.items():
line = line.replace(key,replacewith)
m.write(line)
with open("mod.txt") as f:
print(f.read())
Output:
ip route 192.168.1.0 255.255.0.0 10.10.10.2
ip route 192.168.1.0 255.255.0.0 10.1.1.10
ip route 10.1.1.10 255.255.0.0 10.1.1.30
interface FastEthernet1
ip address 10.1.1.10
duplex auto
speed auto
This will try to replace each line m times (m == len(mapping)) and is not very speedy due to creating lots of intermediate strings (if something got replaced) - it is more a hacky solution to your problem.

You could harness re.sub in this case, following way:
import re
txt = 'ip route 192.168.1.0 255.255.0.0 10.10.10.2\nip route 192.168.1.0 255.255.0.0 1.1.1.2\nip route 1.1.1.1 255.255.0.0 1.1.1.3\ninterface FastEthernet1\nip address 1.1.1.1\nduplex auto\nspeed auto'
out = re.sub(r'1\.1\.1\.([1-3])','10.1.1.\g<1>0',txt)
print(out)
Output:
ip route 192.168.1.0 255.255.0.0 10.10.10.2
ip route 192.168.1.0 255.255.0.0 10.1.1.20
ip route 10.1.1.10 255.255.0.0 10.1.1.30
interface FastEthernet1
ip address 10.1.1.10
duplex auto
speed auto
For simplicity I hardcoded txt, most important line is that of re.sub:
out = re.sub(r'1\.1\.1\.([1-3])','10.1.1.\g<1>0',txt)
It replaces substrings of txt which match first argument with second argument, first argument contain one group ([1-3]) which is later referenced in second argument (\g<1>) thus it in fact of executing following relacement:
1.1.1.1 to 10.1.1.10
1.1.1.2 to 10.1.1.20
1.1.1.3 to 10.1.1.30
However keep in mind that re.sub is working in single-pass fashion, unlike repeating usage of .replace method of str.

Related

Python change ip twice a day at certain time with schedule module, closing prompt

I want my computer to switch between 2 different IP configuration, everyday at two different time. I figured i could automate this with a python script.
import sys
sys.path.append(r"c:\users\user\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\site-packages")
import os
import ipaddress
import socket
import time
import schedule
def firstIP():
hn = socket.gethostname()
ipa = socket.gethostbyname(hn)
print("Current IP: ",ipa)
os.system('netsh interface ip set address name="Wi-Fi" static 192.168.1.20 255.255.255.0 192.168.1.1')
os.system('netsh interface ip set dns name="Wi-Fi" static 8.8.8.8')
os.system('netsh interface ip set dns name="Wi-Fi" static 8.8.4.4 index=2')
print("IP ADRESS CHANGED!")
def secondIP():
hn = socket.gethostname()
ipa = socket.gethostbyname(hn)
print("Current IP: ",ipa)
os.system('netsh interface ip set address name="Wi-Fi" static 192.168.1.50 255.255.255.0 192.168.1.1')
os.system('netsh interface ip set dns name="Wi-Fi" static 8.8.8.8')
os.system('netsh interface ip set dns name="Wi-Fi" static 8.8.4.4 index=2')
print("IP ADRESS CHANGED!")
schedule.every().day.at("01:00").do(firstIP)
schedule.every().day.at("15:00").do(secondIP)
while True:
schedule.run_pending()
time.sleep(1)
I made this script and it seems to work, but for it to run i need to:
open cmd and run the script
leave the cmd open with the script running
is there a way for running the script once, closing the cmd window and the schedule will still work? as a sort of os chron job? or something similar, without leaving the prompt open?
Thank you

Dynamically change a value within a list

I have a list of hostnames which i am using to populate an SCP command to transfer a file from the remote server to my local server. The problem is, some of the hosts do not connect via hostname, and i must use the IP. However this issue with this is that the file i want to transfer has the hostname in the filename, therefore i am using the list of hostnames to capture this, which becomes a problem when we go via the IP rather than the hostname
see code
hostnames = [
'HOST1',
'HOST2',
'1.1.1.1', # HOST3
'2.2.2.2', # HOST4
]
for host in hostnames:
child = pexpect.spawn('scp user#{}:/var/log/{}.backup_file0.{}.gz /home/backups/'.format(host, host))
As you can see, host1 and host 2 will connect and successfully locate the correct file as we have used the hostname, for example:
host1.backup_file0.gz
host2.backup_file0.gz
However host 3 and host 4 will fail due to the hostname in the list being an IP, however the file on these hosts is using the hostname as opposed to an IP
As a note i am not connecting to these devices via ssh so i cannot use the socket to pull the hostname
Is there any way to dynamically map these hostnames / ips and use them as needed?
I don't really see why you couldn't just match the ip with the hostname. Maybe use a dictionary:
hosts = {
'HOST1': 'HOST1',
'HOST2': 'HOST2',
'HOST3': '1.1.1.1', # HOST3
'HOST4': '2.2.2.2', # HOST4
}
for host, ip in hosts.items():
child = pexpect.spawn(f"scp user#{ip}:/var/log/{host}.backup_file0.{date}.gz /home/backups/")
Edit, if I understand correctly you added that you do not have the hostname of hosts for which you connect via IP.
Maybe just try globbing then, as each host surely only backs itself up?
for host in hostnames:
child = pexpect.spawn(f"scp user#{host}:/var/log/*.backup_file0.{date}.gz /home/backups/")

Get Twisted server's IP address

If I have a Twisted server, how can I find its public-facing IP address?
Take this trivial echo server example:
from twisted.internet import protocol, reactor, endpoints
class Echo(protocol.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
server_endpoint = endpoints.serverFromString(reactor, "tcp:1234")
listening_port_deferred = server_endpoint.listen(EchoFactory())
reactor.run()
I was expecting something like server_endpoint.getHost(), but I can't see that TCP4ServerEndpoint offers anything useful.
By adding the following lines before reactor.run(), we can see that the server is listening on all interfaces (0.0.0.0):
def print_host(listening_port):
print("listening_port.getHost():", listening_port.getHost())
listening_port_deferred.addCallback(print_host)
It outputs listening_port.getHost(): IPv4Address(type='TCP', host='0.0.0.0', port=1234). But that doesn't help us with the IP address of the network interface of the server.
We can get the IP address of the client by adding the following as the first line of buildProtocol():
print("Client's address:", addr.host)
But that only gives us the client's address.
How should I get the server's IP address?
Twisted will tell you the address you've bound the server to using just the method you found, getHost on the listening port. Unfortunately, it has the big limitation that you found which is that when the server is listening on all local addresses (INADDR_ANY) it gives you 0.0.0.0 (the canonical IPv4 dotted-quad representation of INADDR_ANY).
When this happens, you have to go outside of Twisted. I've found the netifaces package to be pretty good for this. From the docs:
>>> netifaces.interfaces()
['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0']
>>> >>> addrs = netifaces.ifaddresses('lo0')
>>> addrs[netifaces.AF_INET]
[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}]
By combining this information with the observation that 0.0.0.0 means "all local addresses" you can figure out what local addresses the server will accept connections on.
Thanks to notorious's comment, I realised that the server's IP address is available only once a client connects. However, as Jean-Paul points out, this IP address isn't necessarily public-facing and may well be behind a NATing router.
To obtain the server's IP address for a given connection, we can use the getHost() method from the transport attribute of the Protocol class. This is documented in the ITransport interface.
For example, if we add the following method into the Echo protocol class in the original question, each time a client connects, the server will print out the IP address that was used.
def connectionMade(self):
print("IP address of host given connection:", self.transport.getHost())
So, for example, if you connect from the same machine on which the server is running, you will see:
IP address of host given connection: IPv4Address(type='TCP', host='127.0.0.1', port=1234)
However, if you connect from another machine on the same network, you might see:
IP address of host given connection: IPv4Address(type='TCP', host='192.168.5.103', port=1234)

Python regex to match multiple times, store results separately

I'm a network engineer, trying to dip my toes into programming. I got recommended to try Python.
What I'm trying to do is to save some specific data, matching a string with multiple lines with regexp. We got our data to work with stored in SourceData.
SourceData = '
ip route 22.22.22.22 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 33.33.33.33 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.22.33.44 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.12.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.13.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.14.0 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 44.44.44.0 255.255.255.0 TenGigabitEthernet0/1/0 1.1.1.1'
The number of lines stored in SourceData is always unknown. Could be 0 lines (empty) to unlimited lines.
I want to match all lines containing ipv4-addresses starting with 11.
This is what I've come up with as a start:
ip1 = re.search('11\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}', SourceData)
if ip1:
ip1 = ip1.group()
Verify:
>>> print ip1
11.22.33.44
OK, seems to work. The idea is that when the whole SourceData is matched, with the example provided, the final result for this case would be 4 matches:
ip1 = 11.22.33.44
ip2 = 11.11.12.11
ip3 = 11.11.13.11
ip4 = 11.11.14.0
Next to learn, how do I continue to check SourceData for more matches as described above, and how do I store the multiple matches for use later on in the code? For example, later in the code I would like to use the value from a specific match, lets say match number 4 (11.11.14.0).
I have read some guidelines for Python and Regex, but it seems I quite don't understand it :)
You can use re.findall to return all of the matches
>>> re.findall(r'11\.\d{1,3}\.\d{1,3}\.\d{1,3}', SourceData)
['11.22.33.44', '11.11.12.11', '11.11.13.11', '11.11.14.0']
Several methods, one of them being:
import re
string = """
ip route 22.22.22.22 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 33.33.33.33 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.22.33.44 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.12.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.13.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.14.0 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 44.44.44.0 255.255.255.0 TenGigabitEthernet0/1/0 1.1.1.1'
"""
rx = re.compile(r'^[^\d\n]*(11(?:\.\d+){3})', re.M)
lines = [match.group(1) for match in rx.finditer(string)]
print(lines)
This yields:
['11.22.33.44', '11.11.12.11', '11.11.13.11', '11.11.14.0']
The core here is
^ # match start of the line
[^\d\n]* # NOT a digit or a newline, 0+ times
11 # 11
(?:\.\d+){3} # .0-9 three times
.+ # rest of the line
The rest is done via re.finditer() and a list comprehension.
See a demo on regex101.com.
You can use re.findall with a positive lookbehind to ensure that the correct address, just after "ip route", is being matched:
import re
s = """
ip route 22.22.22.22 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 33.33.33.33 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.22.33.44 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.12.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.13.11 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 11.11.14.0 255.255.255.255 TenGigabitEthernet0/1/0 1.1.1.1
ip route 44.44.44.0 255.255.255.0 TenGigabitEthernet0/1/0 1.1.1.1'
"""
final_ips = re.findall('(?<=ip route\s)11[\d\.]+', data)
Output:
['11.22.33.44', '11.11.12.11', '11.11.13.11', '11.11.14.0']

Extract line that matches with my criteria in Python

I have a variable with this inside:
Device ID: second-02 Entry address(es): IP address: 7.7.7.7
Platform: cisco WS-8PC-S, Capabilities: Router Switch IGMP Interface:
GigabitEthernet0/20, Port ID (outgoing port): GigabitEthernet0/11
Holdtime : 100 sec
Power request id: 0, Power management id: 1, Power available: 0, Power management level: -1 Management address(es): IP address:
7.7.7.7
Device ID: first-01 Entry address(es): IP address: 8.8.8.8 Platform:
cisco ME--12CS-A, Capabilities: Router Switch IGMP Interface:
GigabitEthernet0/11, Port ID (outgoing port): GigabitEthernet0/12
Holdtime : 158 sec Power request id: 0, Power management id: 0, Power available: 0, Power management level: 0 Management address(es): IP address: 8.8.8.8
How can I extract each IP address with its respective Device ID and output something like
Device ID: second-02 = IP address: 7.7.7.7
Device ID: first-01 = IP address: 8.8.8.8
Please note that for each Device ID we have one unique IP address but each one appears two times inside the main variable
So far Ive been able to verify if there is an IP address inside the variable with ip = re.findall( r'[0-9]+(?:\.[0-9]+){3}', s ) but that wont do the trick as I need each Device ID to be paired (or matched?) against its IP address.
How about using this regular expression:
r'Device ID: ([^ ]*) Entry address\(es\): IP address: ([^ ]*)'
https://regex101.com/r/BsIRh1/2

Categories