Python mérés
1. Feladat - Bemelegítő gyakorlatok
A következő részfeladatok célja bemutatni a Python alapvető szintaktikai elemeit és a nyelv használhatóságát, lehetőségeit.
A Python nyelvvel való ismerkedéshez célszerű először egy elszeparált környezetet kialakítani, amiben kedvünkre
telepíthetünk különböző verziójú csomagokat anélkül, hogy azok összeakadnának a globális Python csomagokkal. Ezáltal
rendezetten és elszeparáltan tarthatóak a globális rendszercsomagok és egy könnyen menedzselhető, virtuális környezetet
kapunk. Ehhez telepítenünk kell a virtualenv
Python csomagot a pip3
csomagmenedzselő segítségével. Ha a pip3
nincs
telepítve, először adjuk ki a $ sudo apt-get install python3-pip
parancsot a pip3, majd a
$ sudo pip3 install virtualenv
parancsot a virtualenv
telepítéséhez.
A virtuális mérési környezet létrehozásához adjuk ki a $ virtualenv py-meres
parancsot. Ekkor létrejön egy mappa benne
a szükséges futtatható Python állományokkal és a pip3
csomagmenedzselő függőségeivel. Alapértelmezetten a globálisan
telepített külső csomagok nem kerülnek linkelésre, így minden a Python standard könyvtárában nem szereplő függőség külön
telepítendő, ha szükséges.
Dolgozzunk a létrejött py-meres
mappában. Minden részfeladatot, ahol egy szkript fájlt kell készíteni, egy különálló
.py
kiterjesztésű fájlban oldjuk meg!
A virtuális környezet aktiválásához adjuk ki a mappában a $ source bin/activate
parancsot. Ekkor a prompt elé bekerülő
név jelzi, hogy melyik környezetben dolgozunk.
Környezet deaktiválása: $ deactivate
Ellenőrizzük, hogy elérhető-e a megfelelő Python verzió (>= 3.5.x): $ python3 --version
.
Ha a használt Python verzió nem 3.x, akkor állítsuk be: $ virtualenv –p /usr/bin/python3 py-meres
czentye@ThinkPad-L470:~$ virtualenv py-meres
created virtual environment CPython3.8.5.final.0-64 in 471ms
creator CPython3Posix(dest=/home/czentye/py-meres, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/czentye/.local/share/virtualenv)
added seed packages: pip==21.0.1, setuptools==52.0.0, wheel==0.36.2
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
czentye@ThinkPad-L470:~$ cd py-meres/
czentye@ThinkPad-L470:~/py-meres$ source bin/activate
(py-meres) czentye@ThinkPad-L470:~/py-meres$ python3 -V
Python 3.8.5
(py-meres) czentye@ThinkPad-L470:~/py-meres$
FONTOS! Importálási hibák elkerülésére figyeljünk a következőkre:
- Virtuális környezetben tevékenykedéshez ne felejtsük el aktiválni a környezetet!
- Külső Python csomag virtuális környezetbe telepítésénél ne használjunk rendszeradmin jogosultságot, mert a csomag
abban az esetben a globális Python csomagok közé telepítődik, amelyek alapértelmezetten nem linkelődnek a virtuális
környezetünkbe! Tehát
$
sudopip3 install ...
- Python szkriptet (amit virtuális környezetben futtatunk) ne a szokványos
#!/usr/bin/python3
shebang sorral kezdjük, hanem a rugalmasabb és esetünkben fontos#!/usr/bin/env python3
sorral. Ez előbbi esetben ugyanis közvetlenül a globális Python interpretert jelöljük ki, ami nem láthatja a virtuális környezetbe telepített csomagjainkat. Az utóbbi sorral az env programot hívjuk meg, ami jelen esetünkben már a virtuáis környezetbe linkelt Python interpretert fogja meghívni. Erről magunk is megbizonyosodhatunk, ha meghívjuk a$ which python3
parancsot, ami a virtuális környezeten kívül a/usr/bin/python3
útvonalat, míg aktiválás után a/home/user/py-meres/bin/python3
útvonalat adja vissza.
1.a Feladat - Interaktív mód
Indítsuk el az interpretert a $ python3
paranccsal. Az interpreter érvényes Python kifejezéseket vár, amelyeket
megadás után azonnal feldolgoz és az eredményt (minden Python kifejezésnek van eredménye!) kiírja a konzolra.
Interpreterből kilépéshez használható az exit()
függvényhívást, vagy a kényelmesebb CTRL + D billenytűkombinációt
(EOT - end-of-transmission karakter).
Feladat: A Python nyelv gyakran használt tudományos és matematika algoritmusok implementálására. Írassuk ki mérföldben 3 tizedes jegy pontossággal egy félmaraton hosszát az interpreter segítségével!
- Az arányossági adatokat tároljuk változókban: félmaratoni táv - 21097,5 m; 1 mérföld - 1,6093 km
- Lebegőpontos típusnál tizedes pontot használjunk (
3.14
és nem3,14
)! - Képlet: (táv(m) / 1000 ) / arány (km/mil)
- Kiíratásra használjuk a print() függvényt és sztring formázáshoz a
format()
függvényt (float típus-f
karakter, formátum (link) a segédletben)
Érdekesség: Figyeljük meg a lebegő pontos értékek kerekítési tulajdonságát az eredeti és a 3 tizedes pontosságal kiírt eredményen! (Floating Point Arithmetic: Issues and Limitations)
1.b Feladat - Külső modul használata
Feladat: Írassuk ki a google.com (nem kell a www elé!) tartománynévhez rendelt levelező szervereket (MX rekord)!
A DNS rekordok kezeléséhez használjuk a dnspython
csomagot. A csomagot a $ pip3 install dnspython
paranccsal tudjuk
telepíteni. A DNS rekord lekéréséhez használjuk a dns.resolver
csomag query()
függvényét.
- Használat előtt ne felejtsük el importálni a szükséges csomagot!
- Dokumentáció megtekintéséhez használjuk a
help()
beépített függvényt. Nézzük meg mind a csomag, mind a függvény dokumentációját! - A lekérésnél rekord típusnak adjuk meg az
'MX'
stringet! - A
query()
visszatérési értéke adnspython
csomag egy speciális objektuma (ami adns.resolver.Answer
osztály egy példánya), ami egyben iterálható is. A bejegyzések kiíratásához járjuk be az objektumot a megfelelő nyelvi elemmel! Ne felejtsük el a behúzást a kódblokk esetén!
1.c Feladat - Saját DNS modul
Feladat: Hosszabb kódok esetén az interaktív interpreter használata körülményes. Írjunk Python modult dns_tool.py
néven az előző feladatban megvalósított funkcióhoz!
- dns_tool1.py.txt: Skeleton szkript az
- feladathoz - Nevezzük át a fájlt!
- A rekordok lekérését egy külön függvényben (
get_mx()
) definiáljuk, amely a DNS nevet egy paraméterként kapja meg és az eredményeket egy nagy listába csomagolva adja vissza. - Adjunk minimális dokumentációt a függvényünkhöz!
- A rekordtípus opcionálisan legyen megadható a függvénynek (pl. későbbi továbbfejlesztéshez), ami alapértelmezetten az
'MX'
értéket vegye fel! - A rekordokat egyenként tuple-ben adjuk vissza: (preferencia, név) sorrendben. Az elemekhez az egy darab rekordot
reprezentáló objektum
preference
ésexchange
attribútumán keresztül férhetünk hozzá. A visszaadott struktúra tehát a következő formátumú:[(pref1, exch1), (pref2, exch2), ...]
- A rekord attribútumokat mindenképpen konvertáljuk string típusúvá!
- Hiányzó válasz vagy nem létező rekord esetén a
query()
függvény kivételt dob, amelyek adns.exception.DNSException
leszármazottai. Kezeljük valamilyen módon a kivételt (akár minden kivétel elkapásával) és ekkor egyszerűen térjünk vissza egy üres tuple-lal! - Teszteljük a most megírt modulunkat interaktív módon (indítsunk egy Python interpretert, importáljuk be és hívjuk meg
a függvényt) és nézzük meg a modul/függvény dokumentációkat is a
help()
függvénnyel!
1.d Feladat - Futtatható DNS szkript
Feladat: Tegyük futtathatóvá az elkészült modult!
- dns_tool2.py.txt: Skeleton szkript az
- feladathoz - Nevezzük át a fájlt!
- A szkriptet ne hagyományos (és mondhatni elavult) shebang sorral definiáljuk, hanem a rugalmasabb és esetünkben fontos
env
paranccsal (Lásd az 1. Feladat bevezetőjét)! - Figyeljünk arra, hogy a modul importálható maradjon (ellenőrizzük a
__name__
paramétert)! - Futtatáshoz ne felejtsünk el futtatási jogot adni a szkriptnek (
$ chmod a+x dns_tool2.py, futtatás: ./dns_tool2.py
)! - Modul futtatásakor a DNS nevet interaktív módon, a konzolon keresztül kérjük be a megfelelő beépített függvénnyel
(
input()
)!
2. fealadat - Statisztika szkript
Feladat: Készítsünk futtatható Python szkriptet mirror_stat.py
néven, amely statisztikát készít megadott
Debian mirror szerverek elérhetőségéről, amit a futása végén ki is listáz! A mirror szerverek a következő fájlban:
Primary Debian mirrors vannak megadva,
mentsük le a szkriptünk mellé. Az elérhetőség teszteléséhez telepítsük és használjuk a következő csomagot: ping3
.
A pingeléshez root jogosultság szükséges, így a szkriptet is root jogosultsággal kell indítani!
Viszont a szkript indításához nem használhatjuk csupán a sudo parancsot az első feladatnál leírt
linkelési/importálási problémák miatt. Ennek megkerülésére közvetlenül a virtuális környezetbe linkelt python3
interpretert kell futtatnunk, paraméterül megadva a szkriptünket:
sudo ./bin/python3 mirror_stat.py
. A statisztikában a szerverekhez tartozó RTT értéket tároljuk el!
(Amúgy modern rendszereken a ping
hez nem kell sudo
, feleslegesen ne szívasd meg magad! - blint)
-
mirror_stat.py.txt: Skeleton fájl a 2. feladathoz - Nevezzük át!
-
Töltsük le a mirror szervereket tartalmazó fájlt és mentsük le a virtuális környezeti mappánkba. Fájlkezeléshez használjuk a
with
kulcsszót! -
A statisztika struktúra inicializálásához a beolvasott sorokat először fel kell dolgozni. Pingeléshez csak a domain nevet használjuk, ezért hagyjuk el a
'http://'
elő- és'/.../...'
utótagokat! Használjuk a string változóksplit()
függvényét! Célszerű ehhez a'/'
karakter szerint szeletelni és az így visszaadott listából a megfelelő tagot kiválasztani. -
A statisztika tárolásához használjunk szótárat(dict)!
-
A szerverek elérhetőségéhez használjuk a
ping3
modulping()
függvényét! A használható funkciókat ahelp()
ésdir()
beépített függvények segítségével deríthetjük ki. -
Ellenőrizzük a sikerességet a pingeléskor (Sikertelen ping esetén
False
a visszatérési érték), és a statisztikában mentsük el a visszaadott értéket. Elérhetetlen szerver eseténNone
értéket tároljunk el! -
Egy komplex struktúra formázott kiíratáshoz használjuk a beépített
pprint
csomagpprint()
függvényét! -
Ne felejtsük el futtatáshoz a szkriptet a megfelelő
sudo
paranccsal indítani! (ahogy fent is írtam, nem kellsudo
hozzá - blint)
3. Feladat - Hálózati kommunikáció - port szkenner
A Python standard könyvtárai egyaránt támogatják az alacsony-szintű, socket alapú és magas-szintű hálózatkezelést is.
Az alacsony-szintű hálózatkezelés (CPython és Linux környezet révén) a C nyelvi szintaktikára épül,
ahol a nyelv a Linux socket-kezelő rendszerhívásait ajánlja ki. A követező kép ezt az alacsony-szintű TCP kommunikációt
és az ahhoz meghívandó függvényeket szemlélteti:
- time_server.py.txt: Skeleton fájl a 3. feladathoz - Nevezd át!
HASZNOS PARANCSOK Előfordulhat a szkriptek futtatásánál, hogy a félkész kódok miatt a socket-ek beragadnak és lefoglalják az adott portot/címet. Ekkor a még futó programokat manuálisan kell leállítanunk:
- Nyitott portok listázása:
$ netstat -tuplen
(vagyss -tulpn
modernebb rendszereken - blint) - Futó Python programok kilistázása:
$ ps auxf | grep python
- Program lelövése:
$ sudo kill <PID> vagy a drasztikusabb $ sudo kill -9 <PID>
3.a Feladat - Time szerver
Feladat: Írjunk Python szkriptet time_server.py
néven, ami megvalósít egy pontos időt visszaadó,
TCP alapú szerver funkciót. A szerver a kliensek kapcsolódása után szimplán adja vissza a pontos időt
(a klienseket külön szálon kezelve), majd bontsa a kapcsolatot. A megvalósításhoz használjuk a
Python magas-szintű socketserver
csomagjában definiált ThreadingTCPServer
osztályt.
-
time_server.py.txt: Skeleton fájl a 2. feladathoz
-
A szerver implementációkat és handler osztályokat a socketserver csomagban találjuk.
-
A szerver osztályok csak a kapcsolódást menedzselik a Python alacsony-szintű socket interfészén keresztül. A kéréseket speciális
RequestHandler
osztályok kezelik le. Írjunk saját osztályt a kérések kezeléséhez, amely aBaseRequestHandler
osztályból származzon! -
Nézzünk utána a server és handler osztályok kapcsolatának, működésének és a kérések kezelésének! Definiáljuk felül a
handle()
metódust, amiben rögtön a kapcsolódás után adjuk vissza a pontos időt! -
Üzenet küldéséhez használjuk a
socket
objektumsendall()
metódusát. Ne felejtsük el lezárni a kapcsolatot a válasz után (close()
)! -
A
socket
objektum a handler osztályban arequest
attribútumként érhető el. -
A pontos idő visszaadásához használjuk a
time
csomagot (ctime()
függvény)! -
A socket kommunikációhoz
bytes
típusra konvertálás szükséges. Ehhez használjuk azstr
típus beépítettencode()
függvényét. -
Ne felejtsük el elindítani a szervert a szkriptben, paraméterül megadva a (cím - port) tuple-t és a saját handler osztályunkat! Ehhez példakódot találunk a
socketserver
modul dokumentációjában! -
A szerver IP címét és port számát kérjük be a szkript indítási paramétereként. Ehhez használjuk a beépített
argparse
csomagot! Dokumentáció és példakódok: argparse -
Minden beolvasott paraméter string! Port szám esetén explicite konvertáljuk az értéket
int
típusúra! -
Ctrl-C kombinációval való szabályos kilépéshez kezeljük le a dobott
KeyboardInterrupt
kivételt és állítsuk le a szervert (server.shutdown()
)! -
Teszteléshez használjuk a
telnet
programot. Pl.:$ telnet localhost 8000
3.b Feladat - Port szkenner
Feladat: Valósítsunk meg egy port szkanner funkciót, amely egy megadott hoszton ("www.google.hu") végigszkenneli a "jól ismert" (well-known) portokat (1 - 1023). Eredményként írassuk ki a nyitott portokat. Dolgozzunk a Python konzolban az egyszerűség végett.
- Ehhez használjuk a segédletben emített
scapy
csomagot, amit a következő paranccsal telepíthetünk:pip3 install scapy
- A feladathoz nem kell szkriptet írni, elég interaktívan megvalósítani!
- A
scapy
szintaktikára a segédletben linkelt oldalon találunk példákat vagy használjuk ezt az összefoglalót: scapy_cheatsheet.pdf - A csomagok összeállítására használjunk Layer 2-4 megközelítést (Ethenet + IP + TCP), az összefűzésre
a
/
operátor használható (Basic Packet Crafting / Viewing, Altering Packets blokk). - Érdekességképpen mentsük el a csomagot egy változóban és vizsgáljuk meg az automatikusan kitöltött mezőket
a
.show()
paranccsal. - Lehetőség van egy összefogott csomaghalmaz definiálására is: A vizsgálandó portokat
tuple
ként is megadhatjuk (start
,stop
) formátumban(Altering Packets blokk). Ascapy
képes kezelni a mezőkhöz definiált listákat/tupleket és Descartes-szorzatkét generálja ki az összes csomagot. - Küldésre használjuk a
srp
parancsot (Sending Packets blokk), a szkennelés végeztével állítsuk le a próbálkozást CTRL+C kombinációval vagy a preferáltabb módon adjunk megtimeout
értéket a küldéshez, 1s bőven elég. - Eredményként az
srp
parancs 2 értékkel tér vissza: 1. Sikeres request-response párok, 2. Válasz nélküli kérések. Az eredmények ascapy
saját osztályaiban vannak megvalósítva, de kezelhetőek listaként is a következő formátummal:srp() --> [ [ ( sikeres kérés1, kapott válasz1 ), ( sikeres kérés2, kapott válasz2 ), .... ], [ sikertelen kérések ...] ]
- Eredmény kiíratásához használhatjuk a
.summary()
vagy.show()
függvényeit. - Vegyük a sikeresek listáját, azokból vegyük a válaszokat (Receiving and Analyzing Packets blokk).
A TCP részhez a
.payload
attribútumokon végigjárva vagy az általánosabb[]
operátorral férhetünk hozzá, aminek a kikért payload osztályát kell megadnunk (packet[TCP]
). - Eredményként írassuk ki a kapott válaszok source portját (
.sport
attribútum), azaz a nyitott portokat, amelyekről érkezett válasz.
4. Feladat - Kötelezően választható (1 db) - IMSc plusz feladat (+2 pont)
Az alábbi feladatok közül csak egyet kell kötelezően megoldani a mérés során, mely szabadon választható.
IMSc pont szerezhető mindkét feladat megoldásával!
4.a Feladat - Minimál SSH kliens
Feladat: Írjunk Python szkriptet ssh_client.py
néven, amely egy minimális, interaktív SSH klienst valósít meg.
Az SSH kapcsolat kezeléséhez használjuk a pxssh
modult, amit a pexpect
csomag részeként tudunk telepíteni.
Érdemes megnézni a pxssh
dokumentációját.
-
ssh_client.py.txt
: Skeleton fájl a 4. feladathoz - Nevezd át! -
A scriptet NE indítsuk rendszergazdai jogosultsággal!
-
SSH szerver címét indítási paraméterként adjuk meg!
-
Felhasználónév/jelszó párost kérjük be a konzolról interaktív módon! Jelszó biztonságos bekérésére használjuk a
getpass
modulgetpass()
függvényét, ami nem jeleníti meg a leütött karaktereket! -
Az SSH kapcsolatot kezelő objektum a
pxssh()
függvénnyel kérhető ki, amin alogin()
függvényt meghívva csatlakozhatunk az SSH szerverhez. Példakód az SSH kapcsolatot kezelő osztály dokumentációjában található! -
Kezeljük a hibás kapcsolódásnál felmerülő kivételeket/hibákat!
-
Az interaktív konzolt a prompt-tal együtt a konzolról olvasással valósítsuk meg egy kvázi végtelen ciklusban.
-
q
/Q
karakterekre, illetve CTRL+C kombinációra lépjünk ki! Ne felejtsük el ekkor bontani a kapcsolatot! -
A begépelt parancsokat folyamatosan, az előző parancsokat megőrizve mentsük el a saját ssh.log fájlunkba.
-
A laborban lévő image-ek nagy valószínűséggel futtatnak SSH szervert, amivel tesztelhetjük a kliensünket:
- Hoszt: localhost
- Felhasználó: vagrant
- Jelszó: vagrant