Bash(ujący) w zbożu cz. 3 — namespace

Bash(ujący) 3rd edition

Przestrzenie nazw sieci Linux. Spróbujmy razem zmierzyć się z tym tematem i przemówmy językiem bash(ującego). Ja jestem Piotrek i zapraszam was na trzecią część zmagań z językiem bash.

Zacznijmy od przestrzeni nazw. Czym one tak naprawdę są? Przestrzenie nazw są cechą jądra systemu Linux. Dzieli ona zasoby jądra tak, że jeden zestaw procesów widzi jeden zestaw zasobów, podczas gdy inny zestaw procesów ma inny zestaw zasobów. Funkcja działa poprzez posiadanie tej samej przestrzeni nazw dla tych zasobów w różnych zestawach procesów, ale te nazwy odnoszą się do różnych zasobów. Przykłady nazw zasobów, które mogą istnieć w wielu przestrzeniach, tak że nazwane zasoby są podzielone na partycje, identyfikatory procesów, nazwy hostów, identyfikatory użytkowników, nazwy plików i niektóre nazwy związane z dostępem do sieci i komunikacja międzyprocesowa. Przestrzenie nazw są podstawowym aspektem kontenerów w systemie Linux. System Linux rozpoczyna się od pojedynczego obszaru nazw każdego typu, używanego przez wszystkie procesy. Procesy mogą tworzyć dodatkowe przestrzenie nazw i łączyć różne przestrzenie nazw. Począwszy od wersji 4.10 jądra, istnieje 7 rodzajów przestrzeni nazw. Funkcja przestrzeni nazw jest taka sama we wszystkich rodzajach: każdy proces jest powiązany z przestrzenią nazw i może wyświetlać lub wykorzystywać zasoby powiązane z tą przestrzenią nazw oraz obszary nazw potomków, w stosownych przypadkach. W ten sposób każdy proces (lub jego grupa) może mieć unikalny widok na zasób. Zasób, który jest wyizolowany, zależy od rodzaju przestrzeni nazw, która została utworzona dla danej grupy procesów. My w tej części zajmiemy się przestrzenią nazw dla sieci (network namespace).

Sieciowe przestrzenie nazw wirtualizują stos sieci. Sieciowe przestrzenie nazw pozwalają na stworzenie izolowanej sieci na jednym hoście. Podczas tworzenia przestrzeń nazw sieci zawiera on tylko interfejs pętli zwrotnej. Każdy interfejs sieciowy (fizyczny lub wirtualny) występuje dokładnie w 1 przestrzeni nazw i może być przenoszony między obszarami nazw. Każda przestrzeń nazw będzie miała prywatny zestaw adresów IP, własną tabelę routingu, listę gniazd, tabelę śledzenia połączeń, zaporę i inne zasoby związane z siecią. Po zniszczeniu przestrzeń nazw sieci, niszczy wszystkie wirtualne interfejsy w niej i przenosi fizyczne interfejsy z powrotem do początkowego obszaru nazw sieci. Po uruchomieniu Linuksa będziesz mieć jedną przestrzeń nazw w swoim systemie, a każdy nowo utworzony proces dziedziczy tę przestrzeń nazw od swojego rodzica. Tak więc wszystkie procesy dziedziczą przestrzeń nazw sieci używanych przez init (PID 1).

Zobaczmy jak to wygląda.

Ustawmy dwie proste przestrzenie nazw sieci (namespace network):

sudo ip netns add <nazwa_namespace>
sudo ip netns a <nazwa_namespace>
# czyli
sudo ip netns add namespace1
sudo ip netns add namespace2

Sprawdzić jakie mamy przestrzenie możemy albo przez komendę ip netns:

ip netns
#lub
ip netns list

Albo po przez wyświetlenie katalogu ls /var/run/net/

Materiał wideo dla lubiących konkretne przykłady :)

Jak widzimy tworzenie przestrzeni nazw dla sieci jest proste. Teraz zastanówmy się jak będzie wyglądał nasz skrypt w bash.

  1. Nasze założenie pierwsze - skrypt pozwala nam utworzyć {x} liczbę przestrzeni nazw sieci - przekazywana jako argument do funkcji. Jeżeli istnieją już jakieś przestrzenie nazw nadaje im inne nazwy by utworzyć zadaną liczbę przestrzeni nazw. Nasza baza wyjściowa dla przestrzeni nazw to namespace${i}. Gdzie {x} to liczba utworzonych przestrzeni nazw, a namespace${i} to kolejne nazwy namespace zaczynając od 1 (np.: namespace1).

Mam nadzieje, że nie wystraszyłem was tymi założeniami oraz nie są zbyt zagmatwane. Tak więc zaczynamy, zastanówmy się na początku jak będzie wyglądać nasza funkcja sama tworzące namespace. Na początku nie mamy nić. Mam nadzieję, że każdy pracuje na swoim systemie i wie co już zostało uruchomione/ustawione i nie będzie niespodzianką komunikat.

Cannot create namespace file "/var/run/netns/ns1": File exists

Zobaczmy zatem kod źródłowy naszej funkcji.

# create namespace function
function create_ns()
{
    if [ $# -eq 0 ]; then
        show_message ERROR "Nie podałeś liczby tworzonych namespace wymagana liczba >=1."
        exit 1
    fi
    if [[ $# =~ ^-?[0-9]+$ ]]; then
        for ((i=1; i<=$1; i++)); do
            sudo ip netns add namespace${i}
        done
    else
        show_message ERROR "Nie podałeś liczby >=1."
        exit 2
    fi
    
}

Widzimy, że utworzenie namespace dla sieci nie jest takie trudne. Jedyna nowość to:

if [[ $# =~ ^?[0-9]+$ ]]; then
        for ((i=1; i<=$1; i++)); do
            sudo ip netns add namespace${i}
        done
 else

Zank =~ to tak naprawdę dopasowanie wyrażenia regularnego ciągu po lewej stronie do wzorca wyrażenia po jego prawej stronie. Czyli wyrażenie ^-?[0-9]+$ mówi nam:

  • Znak ^ mówi: Hej to początek wzoru wejściowego
  • Znak - mówi: jestem literałem
  • Znak ? mówi: 0 lub 1 z poprzedzającego
  • Znak + mówi: 1 lub więcej z poprzedzającego [0-9]
  • Znak [0-9] mówi: znaki od 0 do 9.

Warunek ten będzie prawdziwy dla liczb całkowitych dodatnich jak i ujemnych. By obronić się przed liczbami ujemnymi i wyświetlić komunikat możemy dodać:

if [[ $1 =~ ^-?[0-9]+$ && $1 -gt 0 ]]; then

Czyli warunek && (and) o sprawdzeniu czy wartość podana w $1 jest większa od zera. Można wyrażenie regularne przebudować by nie akceptowało minusowych liczb i też skrócić do formy:

if [[ $1 =~ ^[0-9]?+$ && $1 -gt 0 ]]; then

Wtedy będziemy mieli tylko liczby większe od zera brane pod uwagę, wszystkie inne pójdą nam do else.

Teraz podnieśmy sobie trochę porzeczkę i dodajmy nowe namespace. Pamiętajmy tylko, jakie nazwy założyliśmy sobie dla namespace (czyli namespace${i}). Pamiętajmy też, że testując skrypt utworzyliśmy sobie już kilka namespace. Sprawdźmy zatem czy jakieś istnieją.

ip netns list

Otrzymamy listę (jeżeli komenda nic nie zwróci utwórz parę namespace z reki). Pewnie już wiesz jak kod będzie wyglądał? Sprawdźmy czy myślimy podobnie. Zobaczmy zatem kod źródłowy zmodyfikowanej funkcji create_ns(). 

# create namespace function
function create_ns()
{
    if [ $# -eq 0 ]; then
        show_message ERROR "Nie podałeś liczby tworzonych namespace wymagana liczba >=1."
        exit 1
    fi
    if [[ $1 =~ ^-?[0-9]+$ && $1 -ge 0 ]]; then
        for ((i=1, ii=1; i<=$1; i++)); do
            ls /var/run/netns/namespace$ii 2> /dev/null
            while [ $? -eq 0 ]; do
                ((ii++))
                ls /var/run/netns/namespace$ii 2> /dev/null
            done    
            sudo ip netns add namespace${ii}
            ((ii++))
        done
    else
        show_message ERROR "Nie podałeś liczby >=1."
        exit 2
    fi   
}

Jak widzimy mamy kilka zapożyczeń z poprzednich lekcji. Omówmy nasz kod.

W pętli for mamy zmienne $i i $ii. Jeden pilnuje przebiegu pętli zadanego jako pierwszy argument do naszej funkcji $1. Drugi ma za zadanie pilnować nazwy namespaces by się nie powtarzała. Wykorzystujemy znów sztuczkę z kodem błędu zawracanego przez polecenie w tym wypadku ls. I odpowiednio inkrementujemy nasz $ii.

W ten oto sposób do pierwszych dodanych pięciu namespace dodaliśmy sześć kolejnych.

Dobrze stwórzmy teraz menu dla naszego shellowego skryptu i wykorzystajmy nowo utworzoną funkcję create_ns.

Dzięki naszemu shellowemu menu będziemy mogli:

  1. Utworzyć name space.
  2. Pokazać listę namespace i wejść z nimi w interakcje.
  3. Usunąć wszystkie namespace.
  4. Dodać interfejsy sieciowe do ovs (openvswitch)
  5. Utworzyć interfejs bridge na ovs
  6. Wydać dowolną komendę shell
  7. Wyjść ze skryptu.

Zobaczmy nasz kod:

# funkcja main menu
# menu function
function main_menu()
{
#   Opcje dla pętli select.
#   Options for the select loop.
    options="\"Utwórz network namespace\" \"Pokaż namespace\" \"Wyczyść wszystkie namespace\" \"Dodaj interfejsy\" \"Utwórz ovs-switch\" \"Wydaj komendę\" \"Wyjście\""

#   By uniknąć łamania spacji i tworzenia innych zmiennych.
#   To avoid breaking spaces and creating other variables.
    eval set $options

    local all_done=0
    while (( !$all_done )); do
#       Nadajmy troche wyrazu naszemu select.
#       Let's give a little expression to our select.
        PS3='Wybierz opcję: '
#       Nasz select.
#       Our select.
        select option in "$@"
        do
            case "$option" in
                "Utwórz network namespace")
#                   Podanie liczby tworzonych name space.
#                   Specifying the number of names created space.
                    read -p "Podaj liczba namespace którą, mam utworzyć: " ns_number
                    create_ns $ns_number
                    break
                    ;;
                "Pokaż namespace")
#                   Menu listy namespace oraz interakcji z nimi.
#                   Menu namespace list and interact with them.
                    list_of_namespace
                    break
                    ;;
                "Wyczyść wszystkie namespace")
#                   Usówamy powstałe name space.
#                   We delete the resulting namespace.
                    clean_ns
                    break
                    ;;
                "Dodaj interfejsy")
#                   Dodajemy interfejs sieciowy (virtualny)
#                   We add a network interface (virtualny)
                    echo "Nazwy interfejsów sieciowych: "
                    show_interfaces
                    echo "------------------------------------------------"
                    read -p "Podaj nazwę interfejsu sieciowego: " tap_name
                    read -p "Wybierz bridge: " br_name
                    create_internal_ovs_port veth${tap_name} $br_name
                    break
                    ;;
                "Utwórz ovs-switch")
#                   Tworzymy bridge ovs.
#                   We create the ovs bridge.
                    read -p "Podaj nazwe twojego nowego ovs: " ovs_name
                    create_ovs $ovs_name
                    sudo ip l s dev $ovs_name up
                    break
                    ;;
                "Wydaj komendę")
#                   Możemy wydac proste komendy.
#                   Own command
                    read -p "CMD: " cmd
                    $cmd
                    break
                    ;;
                "Wyjście")
#                   Koniec skryptu.
#                   end script
                    local all_done=1
                    show_message OK "Zakończyłeś pracę z narzedziem namespace-creator create by Piotr 'TheRealMamuth' Kośka."
                    press_any_key
                    echo "Pa Pa."
                    break
                    ;;
                *)
                    show_message ERROR "Zły wybór opcji."
                    press_any_key
                    break
                    ;;
            esac
        done
    done
}

Zatem nasza funkcja tworzy nam menu. Przyjrzyjmy się poszczególnym częścią kodu i je omówmy. Nad całością czuwa pętla while w oczekiwaniu na wyjście oraz select tworzącą nam menu. Idąc po kolei w pierwszym case mamy naszą wcześniej już omówiona funkcję create_ns() dodatkowo obudowana składnią związaną z pobraniem zmiennej i przekazanie tej zmiennej jako argument do funkcji. Dalej mamy pokazanie namespace gdzie użyta jest jeszcze nie istniejąca funkcja list_of_namespace(). Wyczyść wszystkie namespace mieści w sobie clean_ns(). Dalej mamy show_interfaces(), create_internal_ovs_port() oraz create_ovs(). Możemy też wydać wbudowane systemowe komendy i wyjść z shellowego menu. Tu na końcu mamy dwie funkcje press_any_key() i show_message() dobrze już znana z części pierwszej i drugiej bash(ującego).

Napiszmy brakujące funkcje realizujące odpowiednio zadania im powierzone. 

Konserwator powierzchni namespacesowych

Nasz funkcja do czyszczenia / usuwania namespace.

# clean namespace function
function clean_ns()
{
    for element in `ip netns list`; do
        sudo ip netns del $element
    done
}

 Prawda, że proste. Opiszmy zatem co nasz konserwator powierzchni namespesowych zrobił:

  • Dzięki funkcji for wykonaliśmy operacje (sudo ip netns del) na każdym z elementów z polecenia `ip netns list`. Tak Tak. polecenie ip net list zwraca nam policzalne elementy (iteracje) a skoro wiemy że for dobrze działa na elementach iteracyjnych grzechem było by nie skorzystać. 

Musimy też zwrócić uwagę czy jakiś proces uruchomiony w kontekście namespace nie będzie korzystał z interfejsu sieciowego. Usunięcie namespace podczas tak aktywnie wykorzystywanego interfejsu przez jaki kolwiek proces uruchomiony w namespace spowoduje niedostępność tego interfejsu. Nalep szczegolnie uważać przy przekazywaniu fizycznych interfejsów. 

Umiemy już tworzyć namespace i je niszczyć. Teraz zróbmy coś z nimi. Dodajmy do naszego menu obsługę namespaców. 

list_of_namespace()

Funkcja:

# Funkcja do wyświetlania namespace
# Function to display namespace
function list_of_namespace()
{
    local all_done=0
    while (( !$all_done )); do
        options2=""
        for element in `ip netns list; echo "exit"`; do
            options2="$options2 $element"
        done

        PS3='[namespace]: '

        eval set $options2

#       Dodatkowo gdy wybierzemy opcje wchodzi w interakcje z namespace.
#       In addition, when we select options interact with namespace.
        select option2 in "$@"
        do
            if [[ $option2 == "exit" ]]; then
                local all_done=1
                break
            fi
            if [[ $option2 == "$option2" ]]; then
                action_namespace
                break
            fi
        done
    done
}

Mamy tu podobne zastosowanie pętli while jak w przypadku sekcji menu. Dodatkowo w tej funkcji jest stworzona specjalna zmienna options2 która, jest pusta po to by uzupełnić ja dynamicznymi nazwami name space. Musimy mieć na uwadze, że namespace możemy dodawać ile chcemy. Tak wiec menu to jest budowane za każdym przebiegiem pętli while.Dodatkowo tez potrzebujemy opcji Exit by wyjść z naszego pod menu. W pętli select mamy tylko dwa if. Jeden pilnuje pozycji "exit" drugi pozostałych (Jakby tak drugą instrukcję if wziąć osobno pod uwagę to ona także była by poprawna dla wartości exit. jednak nie mamy takiego namespace - pamiętamy nasze założenia o konstrukcji namespace = namespace${ii}). I tak w przypadku gdy podamy numer odpowiadający namespace to przejdziemy do action_namespace(). Czyli wywołamy funkcje. Wywołana jest bez parametru gdyż skorzystamy z zmiennych globalnych. Pętla while zagwarantuje nam, że w momencie wejścia w dany name space będziemy mieli w aktualnym przebiegu pętli akurat ta zmienną którą potrzebujemy.

Dodatkowa zagadka dla osób śledzących moje materiały :). Specjalnie dodałem za dużo do tej funkcji - postaracie się ja odchudzić :). Jedno z kilku rozwiązań przedstawię w kolejnym wpisie. Wróćmy do naszych funkcji :).

action_namespace()

Funkcja ta realizuje dość ważną rolę. Pozwala ona w naszym skrypcie na zarządzanie naszymi namespace po przez dodanie utworzonych interfejsów sieciowych do namespace. Ustawienie na tych interfejsach adresu IP. Wyczyszczenia adresu IP:

# Akcje wykonywane w danym namespace
# Actions performed in given namespace
function action_namespace()
{
    local all_done=0
    while (( !$all_done )); do
#       Opcje dla select.
#       Options for select.
        actions_ns="\"Dodaj interfejs sieciowy\" \"Ustaw adres IP\" \"Wyczyść adres IP\" \"Wydaj komendę\" \"Wyjście\""
#       Indywidualny znak zachety.
#       Individual sign of incentives.
        PS3="[$option2]: "
#       Dany wybrany z opcji list_of_namespace()
#       Data selected from list_of_namespace ()
        ns=$option2
        eval set $actions_ns
        select action in "$@"
        do
            case "$action" in
                "Dodaj interfejs sieciowy")
#                   Wyświetla liste dostepnych interfejsów sieciowych oraz pozwala dodac wybrany do namespace.
#                   Displays a list of available network interfaces and allows you to add selected to namespace.
                    show_interfaces
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    sudo ip link set $if_set netns $ns 
                    break
                    ;;
                "Ustaw adres IP")
#                   Pozwala ustawić adresi ip na interfejsie sieciowym w danym name space.
#                   Allows you to set the IP address and p on the network interface in the given name space.
                    sudo ip netns exec $ns ip -o link show | awk '{print $2,$9}'
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    read -p "Podaj adres ip (np.: 10.10.10.10/24): " ip_set
                    sudo ip netns exec $ns ip a a $ip_set dev $if_set
                    sudo ip netns exec $ns ip l s dev $if_set up
                    break
                    ;;
                "Wyczyść adres IP")
#                   Pozwala zwolic adres ip interfejsu sieciowego w danym namespace.
#                   Allows you to enable the ip address of the network interface in the given namespace.
                    show_interfaces
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    sudo ip netns exec $ns ip a f dev $if_set
                    sudo ip netns exec $ns ip l s dev $if_set down
                    break
                    ;;
                "Wydaj komendę")
#                   Own command in namespace.
                    read -p "Podaj komendę (np.: ping 10.10.10.10):" my_command
                    sudo ip netns exec $ns $my_command
                    break
                    ;;
                "Wyjście")
                    local all_done=1
                    break
                    ;;
            esac
        done
    done
}

Mamy tu proste menu select z sztywno ustalonymi opcjami. Menu jest przez nas zdefiniowane i nie jest tworzone dynamicznie jak to związane z namespace (nazwami do wyświetlenia). Spójrzmy na poszczególne case i zobaczmy co one realizują.

Dodanie interfejsu sieciowego do namespace. Jak już wiemy jeden interfejs może znajdować się tylko w jednym namespace. Ta opcja pozwala nam podejrzeć interfejsy dostępne w głównym namespace oznaczonym jako default powstałym przy uruchomieniu pierwszego procesu init. 

sudo ip link set $if_set netns $ns

i tak poleceniem ip link set podpinamy wskazany interfejs w zmiennej $if_set do namespace przekazany w zmiennej $ns.

Ustawienie adresu ip

sudo ip netns exec $ns ip -o link show | awk '{print $2,$9}'
sudo ip netns exec $ns ip a a $ip_set dev $if_set
sudo ip netns exec $ns ip l s dev $if_set up

Na początku pobieramy nazwę dostępnych interfejsów w ramach konteksu namespace w którym, działamy. Dalej ustawiamy wskazany adres ip $ip_set dla urządzenia ze zmiennej $if_set. Na koniec podnosimy nas interfejs z statusu DOWN na UP. W wyczyść adres ip mamy odwrotną sytuację. Czyścimy adres ip i ustawiamy na interface status down. 

Wydanie komendy pozwala na wydanie komendy w kontekście namespace. Proponuje wydać komendę na początku `ip a` a potem `sudo ovs-vsctl show`. Powinieneś już rozumieć trochę zawiły początek. 

Pozostałe funkcje

Funkcja show_interfaces()

Funkcja ta jest dobrze znana osoba które śledzą mój kanał na YouTube. Polecenie z tej funkcji było prezentowane na poprzedniej lekcji. Ma ono za zadanie wyświetlić nazwy interfejsów sieciowych z statusem up lub down.

# Pokazuj dostepne interfejsy sieciowe
# Show network interfaces - in default namespace (linux)
function show_interfaces()
{
    ip -o link show | awk '{print $2,$9}'
}

Funkcja press_any_key()

Tu też dobrze znane polecenie. Oczekuje na dowolny klawisz ze strony użytkownika. Swoista "Pauza".

# Prosta funkcja "Pauza", kontynuacja na dowolny przycisk
# any button function
function press_any_key()
{
    read -n 1 -s -r -p "Naciśnij dowolny klawisz by kontynuować..."
}

Funkcja create_ovs()

Tworzenie openvswitcha.

# funkcja tworzy prosty bridge ovs
# function creates a simple ovs bridge
function create_ovs()
{
    sudo ovs-vsctl add-br $1
}

Funkcja create_internal_ovs_port()

dodawanie portu do tego switcha.

# Funkcja dodaje port wewnetrzny do ovs
# The function adds the internal port to ovs
function create_internal_ovs_port()
{
    sudo ovs-vsctl add-port $2 $1 -- set Interface $1 type=internal
}

Mam nadzieje, że o nikim nie zapomniałem :);)

main

Kod źródłowy. Prezentuje na koniec kod dla wszystkich spięty w całość. Można go wypróbować lub przerobić. Wyrazić konstruktywna krytykę jak i destruktywną :). Zawsze świeża zawartość z poprawkami na moim koncie GitHub.com. Zapraszam.

#!/bin/bash -       
#title           : mnamespace
#description     : Functions that create namespace
#author		     : Piotr "TheRealMamuth" Kośka
#date            : 24.02.2018
#version         : v1.0   
#usage		     :
#notes           :
#bash_version    : 4.4.12(1)-release
#editor          : visual studio code
#==============================================================================

# lib
. ./infoshow.sh
. ./pacman.sh


# Pokazuj dostepne interfejsy sieciowe
# Show network interfaces - in default namespace (linux)
function show_interfaces()
{
    ip -o link show | awk '{print $2,$9}'
}

# Prosta funkcja "Pauza", kontynuacja na dowolny przycisk
# any button function
function press_any_key()
{
    read -n 1 -s -r -p "Naciśnij dowolny klawisz by kontynuować..."
}

# Funkcja usówa wszystkie namespace 
# clean namespace function
function clean_ns()
{
    for element in `ip netns list`; do
        sudo ip netns del $element
    done
}

# funkcja tworzy namespace
# create namespace function
function create_ns()
{
    if [ $# -eq 0 ]; then
#       
        show_message ERROR "Nie podałeś liczby tworzonych namespace wymagana liczba >=1."
        exit 1
    fi
#   Wyrażenie regularne. Sprawdza czy podany argument do funkcji jako $1 jest liczbą oraz czy jest wiekszy 0.
#   Regular expression. Checks whether the argument given to the function as $ 1 is a number and whether it is greater than 0.
    if [[ $1 =~ ^-?[0-9]+$ && $1 -ge 0 ]]; then
        for ((i=1, ii=1; i<=$1; i++)); do
#           Sprawdza czy dany namespace istnieje.
#           Check if namespace exists
            ls /var/run/netns/namespace$ii 2> /dev/null
            while [ $? -eq 0 ]; do
#               Jeżeli istnieje do inkrementuj.
#               If exists set ii++
                ((ii++))
                ls /var/run/netns/namespace$ii 2> /dev/null
            done
#           Tworzy name space.   
#           Create namespace. 
            sudo ip netns add namespace${ii}
            ((ii++))
        done
    else
#       Gdy nie podamy liczby
#       When we put someting else not number.
        show_message ERROR "Nie podałeś liczby >=1."
        exit 2
    fi   
}

# Akcje wykonywane w danym namespace
# Actions performed in given namespace
function action_namespace()
{
    local all_done=0
    while (( !$all_done )); do
#       Opcje dla select.
#       Options for select.
        actions_ns="\"Dodaj interfejs sieciowy\" \"Ustaw adres IP\" \"Wyczyść adres IP\" \"Wydaj komendę\" \"Wyjście\""
#       Indywidualny znak zachety.
#       Individual sign of incentives.
        PS3="[$option2]: "
#       Dany wybrany z opcji list_of_namespace()
#       Data selected from list_of_namespace ()
        ns=$option2
        eval set $actions_ns
        select action in "$@"
        do
            case "$action" in
                "Dodaj interfejs sieciowy")
#                   Wyświetla liste dostepnych interfejsów sieciowych oraz pozwala dodac wybrany do namespace.
#                   Displays a list of available network interfaces and allows you to add selected to namespace.
                    show_interfaces
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    sudo ip link set $if_set netns $ns 
                    break
                    ;;
                "Ustaw adres IP")
#                   Pozwala ustawić adresi ip na interfejsie sieciowym w danym name space.
#                   Allows you to set the IP address and p on the network interface in the given name space.
                    sudo ip netns exec $ns ip -o link show | awk '{print $2,$9}'
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    read -p "Podaj adres ip (np.: 10.10.10.10/24): " ip_set
                    sudo ip netns exec $ns ip a a $ip_set dev $if_set
                    sudo ip netns exec $ns ip l s dev $if_set up
                    break
                    ;;
                "Wyczyść adres IP")
#                   Pozwala zwolic adres ip interfejsu sieciowego w danym namespace.
#                   Allows you to enable the ip address of the network interface in the given namespace.
                    show_interfaces
                    echo "-----------------------------------------------------"
                    read -p "Wybierz interfejs: " if_set
                    sudo ip netns exec $ns ip a f dev $if_set
                    sudo ip netns exec $ns ip l s dev $if_set down
                    break
                    ;;
                "Wydaj komendę")
#                   Own command in namespace.
                    read -p "Podaj komendę (np.: ping 10.10.10.10):" my_command
                    sudo ip netns exec $ns $my_command
                    break
                    ;;
                "Wyjście")
                    local all_done=1
                    break
                    ;;
            esac
        done
    done
}

# funkcja tworzy prosty bridge ovs
# function creates a simple ovs bridge
function create_ovs()
{
    sudo ovs-vsctl add-br $1
}

# Funkcja dodaje port wewnetrzny do ovs
# The function adds the internal port to ovs
function create_internal_ovs_port()
{
    sudo ovs-vsctl add-port $2 $1 -- set Interface $1 type=internal
}

# Funkcja do wyświetlania namespace
# Function to display namespace
function list_of_namespace()
{
    local all_done=0
    while (( !$all_done )); do
        options2=""
        for element in `ip netns list; echo "exit"`; do
            options2="$options2 $element"
        done

        PS3='[namespace]: '

        eval set $options2

#       Dodatkowo gdy wybierzemy opcje wchodzi w interakcje z namespace.
#       In addition, when we select options interact with namespace.
        select option2 in "$@"
        do
            if [[ $option2 == "exit" ]]; then
                local all_done=1
                break
            fi
            if [[ $option2 == "$option2" ]]; then
                action_namespace $option2
                break
            fi
        done
    done
}

# funkcja main menu
# menu function
function main_menu()
{
#   Opcje dla pętli select.
#   Options for the select loop.
    options="\"Utwórz network namespace\" \"Pokaż namespace\" \"Wyczyść wszystkie namespace\" \"Dodaj interfejsy\" \"Utwórz ovs-switch\" \"Wydaj komendę\" \"Wyjście\""

#   By uniknąć łamania spacji i tworzenia innych zmiennych.
#   To avoid breaking spaces and creating other variables.
    eval set $options

    local all_done=0
    while (( !$all_done )); do
#       Nadajmy troche wyrazu naszemu select.
#       Let's give a little expression to our select.
        PS3='Wybierz opcję: '
#       Nasz select.
#       Our select.
        select option in "$@"
        do
            case "$option" in
                "Utwórz network namespace")
#                   Podanie liczby tworzonych name space.
#                   Specifying the number of names created space.
                    read -p "Podaj liczba namespace którą, mam utworzyć: " ns_number
                    create_ns $ns_number
                    break
                    ;;
                "Pokaż namespace")
#                   Menu listy namespace oraz interakcji z nimi.
#                   Menu namespace list and interact with them.
                    list_of_namespace
                    break
                    ;;
                "Wyczyść wszystkie namespace")
#                   Usówamy powstałe name space.
#                   We delete the resulting namespace.
                    clean_ns
                    break
                    ;;
                "Dodaj interfejsy")
#                   Dodajemy interfejs sieciowy (virtualny)
#                   We add a network interface (virtualny)
                    echo "Nazwy interfejsów sieciowych: "
                    show_interfaces
                    echo "------------------------------------------------"
                    read -p "Podaj nazwę interfejsu sieciowego: " tap_name
                    read -p "Wybierz bridge: " br_name
                    create_internal_ovs_port veth${tap_name} $br_name
                    break
                    ;;
                "Utwórz ovs-switch")
#                   Tworzymy bridge ovs.
#                   We create the ovs bridge.
                    read -p "Podaj nazwe twojego nowego ovs: " ovs_name
                    create_ovs $ovs_name
                    sudo ip l s dev $ovs_name up
                    break
                    ;;
                "Wydaj komendę")
#                   Możemy wydac proste komendy.
#                   Own command
                    read -p "CMD: " cmd
                    $cmd
                    break
                    ;;
                "Wyjście")
#                   Koniec skryptu.
#                   end script
                    local all_done=1
                    show_message OK "Zakończyłeś pracę z narzedziem namespace-creator create by Piotr 'TheRealMamuth' Kośka."
                    press_any_key
                    echo "Pa Pa."
                    break
                    ;;
                *)
                    show_message ERROR "Zły wybór opcji."
                    press_any_key
                    break
                    ;;
            esac
        done
    done
}

main()
{
    main_menu
}

Na sam koniec materiał video z prezentacji działania skryptu. Dodatkowo chwiałbym podziękować wszystkim którzy, wychwycili błędy w poprzednich moich wpisach. Do usłyszenia ponownie w bash(ującym).

Bibliografia

Przystępna i zrozumiała dla każdego

  1. http://abregman.com/2016/09/29/linux-network-namespace/
  2. https://blog.scottlowe.org/2013/09/04/introducing-linux-network-namesp...
  3. https://www.systutorials.com/docs/linux/man/8-ip-netns/
  4. https://blogs.igalia.com/dpino/2016/04/10/network-namespaces/
  5. https://www.opencloudblog.com/?p=66
  6. https://linuxpolska.pl/zabawa-w-namespaces/
  7. https://en.wikipedia.org/wiki/Linux_namespaces
  8. http://hintcafe.net/post/78293519027/running-a-process-inside-a-networ...