systemu operacyjnego, pod kontrolą którego będą pracować maszyny wirtualne. Celem tej pracy było:
\begin{itemize}
\item napisanie programu serwera obrazów maszyn wirtualnych, którego zadaniem jest ich rejestrowanie, przypisywanie oraz dystrybuowanie
\item stworzenie klienta synchronizującego stan maszyny klienckiej ze stanem obecnym na serwerze, pobierającego obrazy, wyświetlającego ekran wyboru systemy do uruchomienia oraz obsługującego ich uruchmianie poprzez mechanizm QEMU wraz z KVM
\item przygotowanie konfiguracji dla serwera opartego o system operacyjny Linux:
\begin{itemize}
\item do automatycznego instalowania systemu nadzorcy dla maszyn klienckich
\item do obsługi sieciowej podłączonych maszyn (przydzielanie adresów IP, wskazywanie na serwer konfiguracji)
\item do zarządzania maszynami klienckimi
\end{itemize}
\item wdrożenie rozwiązania w symulowanym środowisku testowym
\end{itemize}
\paragraph{}
Prezentowana praca podzielona została na kilka rozdziałów:
\item wymagania i narzędzia: jakie są wymagania dla aplikacji, jakie narzędzia zostały wykorzystane do rozwiązania problemu, ich opis oraz uzasadnienie wyboru
\item specyfikacja zewnętrzna: specyfikacja opisująca całe rozwiązanie z perspektywy użytkowników końcowych, z wyszczególnionymi elementami składowymi rozwiązania
\item specyfikacja wewnętrzna: specyfikacja opisująca techniczne aspekty rozwiązania, z perspektywy osoby technicznej zaznamiającej się z kodem źródłowym oprogramowania
\item weryfikacja i walidacja: wyniki testów przeprowadzonych na testowym środowisku wdrożeniowym, ich analiza
\item podsumowanie i wnioski: prezentacja wniosków wynikających z analizy wyników testów, krytyka proponowanego rozwiązania i propozycje poprawek
serwer SFTP sprawdza dostęp do maszyny klienta poprzez SSH (Secure SHell). Jeżeli test przebiegnie pomyślnie, nawiązywane jest połączenie SFTP, przez
które rozpoczyna się transfer plików. Protokół obsługuje przesył wielowątkowy, gdzie każda operacja transferu ma przypisany unikalny numer
identyfikacyjny, nadawany przez klienta, dzięki czemu serwer może odpowiadać na zapytania klienta asynchronicznie i bez zachowania kolejności.
Aby poprawić wydajność, klient wysyła często wiele zapytań zanim zatrzymuje się i czeka na odpowiedzi.\cite{bib:ssh-article}
\paragraph{SSH}
SSH (Secure SHell) to standard protokołów, który miał w założeniu stanowić bezpieczną alternatywę dla protokołu telnet.
Do autoryzacji wykorzystuje kryptografię klucz publiczy - klucz prywatny, gdzie w najprostszej postaci
pary kluczy sa generowane automatycznie i służą do zabezpieczenia połączenia sieciowego, natomiast sama autoryzacja
odbywa się za pomocą hasła użytkownika systemu zdalnego.\cite{bib:ssh-rfc} Obecnie najpopularniejszą implementacją protokołu SSH jest OpenSSH, stworzona przez
część członków projektu OpenBSD. Jest wykorzystywana przez większość najpopularniejszych systemów operacyjnych, takich jak
różne dystrybucje Linux, macOS, Microsoft Windows, czy OpenBSD.\cite{bib:openssh-usage}
\paragraph{Serwer HTTP}
Protokół HTTP (HyperText Transfer Protocol) powstał jako część WWW (World Wide Web) i miał służyć do transferu dokumentów hipertekstowych.
Prace nad nim były prowadzone przez Tima Berners-Lee zatrudnionego w CERN (Conseil Européen pour la Recherche Nucléaire) od 1989 roku
(pierwsza propozycja systemu hipertekstowego opartego o Internet),
gdzie implementacja nastąpiła w 1990 roku, by już rok później pojawiły się pierwsze serwery poza organizacją macierzystą twórcy\cite{bib:http-history}.
W 1997 roku powstała pierwsza ustandaryzowana wersja protokołu, HTTP/1.1, która została wykorzystana w tej pracy.\cite{bib:http-rfc}
HTTP to obecnie bardzo wszechstronne narzędzie, pozwalające obecnie na przesyłanie dowolnych danych poprzez sieć Internet.
Jako protokół jest podstawą współczesnej sieci Web, służy do obustronnego transferu danych pomiędzy serwerami i klientami.
Komunikacja jest rozpoczynana zawsze przez klienta. Tok takiej tranzakcji jest następujący (opis dotyczy wersji HTTP/1.1):
\item otwarcie połączenia TCP (Transmission Control Protocol) z serwerem
\item wysłanie zapytania HTTP
\item odebranie odpowiedzi HTTP od serwera
\item zamknięcie połączenia, lub ponowne jego użycie (do wysłania kolejnego zapytania)
\end{itemize}
Wiadomości HTTP (dla wersji HTTP/1.1 i starszych) mają ustandaryzowany format, czytelny dla człowieka i zapisywalny w postaci tekstu.
Format różni się dla zapytań i odpowiedzi, gdzie zapytanie posiada następujące pola\cite{bib:http-overview}:
\begin{itemize}
\item metoda HTTP: definiuje operację, którą chce przeprowadzić klient. Może to być np. operacja pobrania danych (metoda GET), czy wysłania danych (metoda POST). Standard HTTP/1.1 zawiera poniższe metody\cite{bib:http-methods}:
\begin{itemize}
\item CONNECT
\item DELETE
\item GET
\item HEAD
\item OPTIONS
\item POST
\item PUT
\item TRACE
\end{itemize}
\item ścieżka dostępu do danych
\item wersja protokołu HTTP
\item opcjonalne nagłówki
\item ciało zapytania, dla metod obsługujących przesył danych z klienta do serwera
\end{itemize}
Natomiast struktura odpowiedzi jest następująca\cite{bib:http-overview}:
\begin{itemize}
\item wersja protokołu HTTP
\item kod statusu: jeden z kodów HTTP oznaczający czy zapytanie zostało obsłużone oraz jaki był efekt jego obsługi
\item wiadomość statusu: krótka wiadomość opisująca znaczenie kodu statusu
\item nagłówki HTTP, podobnie do zapytania
\item ciało odpowiedzi, jeżeli wymagane
\end{itemize}
Z perspektywy prezentowanego projektu ważną cechą protokołu HTTP jest możliwość przesyłu dowolnych danych w ten sam sposób. Zostało to wykorzystane do
stworzenia serwera, który potrafi przesyłać dane do klientów w formacie JSON (JavaScript Object Notation), czyli tekstowym formacie
opisującym (w tym konkretnym przypadku) konfigurację oraz w formacie binarnym, co jest wykorzystywane do przesyłu obrazów maszyn wirtualnych.
Powyższe rozwiązanie zostało wybrane właśnie z powodu łatwości połączenia części przesyłającej konfigurację z serwera do klientów oraz części
przesyłającej obrazy. W dalszej części pracy opisano dokładną architekturę tego rozwiązania, jej zalety oraz wady.
\paragraph{Maszyny wirtualne}
Maszyna wirtualna to w najprostszym ujęciu programowa implementacja fizycznego urządzenia, która wykonuje zadany program w sposób identyczny
do tego urządzenia. Aplikacja, która to umożliwia jest nazywana nadzorcą (ang. hypervisor). Jej zadaniem jest tłumaczenie zapytań programów
do natywnego sprzętu na zapytania do sprzętu, na którym działa uruchomiona maszyna wirtualna. Istnieje kilka popularnych rodzajów wirtualizacji\cite{bib:virtualization-article}:
\begin{itemize}
\item pełna wirtualizacja z translacją binarną, rys. \ref{fig:fullvirt-diagram} przedstawia schemat działania takiego systemu
\item wirtualizacja ze wsparciem sprzętowym, rys. \ref{fig:hwvirt-diagram} przedstawia schemat działania takiego systemu
\item wirtualizacja wspierana przez system operacyjny i parawirtualizacja, rys. \ref{fig:paravirt-diagram} przedstawia schemat działania takiego systemu
W prezentowanym projekcie wykorzystano model ze sprzętowym wsparciem dla wirtualizacji poprzez KVM (Kernel Virtual Machine) wraz z QEMU (Quick EMUlator) i technologie
Intel VT-d/AMD-V, pod kontrolą systemu operacyjnego Ubuntu Linux w wersji 22.04 LTS.
Wirtualizacja urządzeń We/Wy jest realizowana poprzez mechanizm IOMMU (Input/Output Memory Management Unit). Działa on na analogicznej zasadzie co MMU, zapewniając kontrolę
oraz tłumaczenie adresów logicznych urządzeń do ich adresów fizycznych.\cite{bib:amd64-specs} Pozwala to na przekierowanie
urządzenia bezpośrednio pod kontrolę systemu operacyjnego uruchomionego w maszynie wirtualnej, pomijając sterownik systemu zarządcy.
Technologia ta może zostać wykorzystana do przekazania w taki sposób urządzeń wymagających bardzo niskich opóźnień i dużej przepustowości,
na przykład kart graficznych, lub kart sieciowych. Wtedy pełną kontrolę nad sprzętem otrzymuje system operacyjny gościa w maszynie
wirtualnej, a narzut wirtualizacji na wydajność jest zminimalizowany.
\paragraph{DHCP}
Protokół DHCP (Dynamic Host Configuration Protocol) odpowiada za automatyczne przypisywanie adresów IP oraz wysyłanie konfiguracji
sieciowej do urządzeń. Jego założeniem była także iteroperatywność z już wtedy istniejącym protokołem BOOTP (BOOTstrap Protocol), przez co
pojedyncze paczki danych (nazwane wiadomościami) są te same dla obu protokołów (z kilkoma odstępstwami).\cite{bib:dhcp-rfc}
Format wiadomości DHCP jest następujący:\cite{bib:dhcp-rfc}
\begin{itemize}
\item OP (1 bajt): typ wiadomości
\item HTYPE (1 bajt): typ adresu karty sieciowej
\item HLEN (1 bajt): długość adresu karty sieciowej
\item HOPS (1 bajt): ustawiane przez klienta na 0 (zero); używane przez agentów pośredniczących
\item XID (4 bajty): numer identyfikacyjny (ID) tranzakcji; używany do rozpoznania wiadomości przeznaczonych dla konkretnego klienta
\item SECS (2 bajty): liczba sekund od rozpoczęcia procesu odnawiania lub nabywania adresu; ustawiane przez klienta
\item FLAGS (2 bajty): flagi
\item CIADDR (4 bajty): adres IP klienta; uzupełniany tylko, jeżeli klient może odpowiedzieć na zapytanie ARP (Address Resolution Protocol) i jest w stanie BOUND, RENEW, lub REBINDING
\item YIADDR (4 bajty): ,,twój'' (klienta) adres IP
\item SIADDR (4 bajty): adres następnego serwera do użycia podczas pobierania
Zdefiniowano następujące wiadomości w protokole DHCP:\cite{bib:dhcp-rfc}
\begin{itemize}
\item DHCPDISCOVER - Klient wysyła pakiet rozgłoszeniowy, aby zlokalizować dostępne serwery
\item DHCPOFFER - Serwer odpowiada klientowi na zapytanie DHCPDISCOVER z proponowaną konfiguracją
\item DHCPREQUEST - Klient odpowiada serwerowi (a) żądając oferowanych parametrów od jednego serwera i ignorując pozostałe, (b) potwierdzając prawidłowość otrzymanej wcześniej konfiguracji, lub (c) przedłużając dzierżawę adresu IP
\item DHCPACK - Serwer odpowiada klientowi parametrami konfiguracji, wraz z przyznanym adresem IP
\item DHCPNAK - Serwer odpowiada klientowi zaznaczając, że konfiguracja żądana przez klienta jest nieprawidłowa, lub dzierżawa adresu wygasła
\item DHCPDECLINE - Klient odpowiada serwerowi, że dany adres sieciowy jest już w użytku
\item DHCPRELEASE - Klient odpowiada serwerowi, zwalniając dany adres sieciowy i anulując bieżącą dzierżawę
\item serwer wysyła klientowi proponowaną konfigurację poprzez wiadomość \texttt{DHCPOFFER}
\item klient żąda zadanej konfiguracji poprzez wiadomość \texttt{DHCPREQUEST}
\item serwer odpowiada klientowi wiadomością \texttt{DHCPACK}, jeżeli żądana konfiguracja jest poprawna, w przeciwnym przypadku odpowiada \texttt{DCHPNAK}
\item przy odłączaniu od sieci, klient wysyła wiadomość \texttt{DHCPRELEASE}, zwalniając zajmowany adres sieciowy
\end{itemize}
\paragraph{TFTP}
TFTP (Trivial File Transfer Protocol) to nieskomplikowany protokół przesyłu plików. Jego jedynym zadaniem jest
odczyt pliku po stronie serwera i przesłanie go do klienta. Nie obsługuje listowania plików, ani uwierzytelniania.\cite{bib:tftp-rfc}
\item\texttt{NETASCII}, czyli zmodyfikowana forma formatu \texttt{ASCII}, rozszerzona do długości 8 bitów i zawierająca dodatkowe znaki kontrolne
\item\texttt{OCTET}, czyli format przesyłu surowych bajtów danych
\item\texttt{MAIL}, uznany za przestarzały w specyfikacji RFC1350\cite{bib:tftp-rfc}
\end{itemize}
Proces transferu plików jest następujący\cite{bib:tftp-rfc}:
\begin{itemize}
\item klient wysyła zapytanie RRQ (read request/żądanie odczytu), lub WRQ (write request/żądanie zapisu), zawierające nazwę pliku i tryb transferu do serwera na port 69
\item serwer odpowiada pakietem OPTIONS ACK (jeżeli były użyte) i pakietem ACK w przypadku zapytania WRQ; w przypadku zapytania RRQ odpowiedź to od razu pierwszy pakiet żądanych danych
\item maszyna będąca źródłem pliku wysyła go w ponumerowanych segmentach (domyślnie o wielkości 512 bajtów); maszyna odbierająca na każdy segment odpowiada pakietem ACK
\item ostatni segment jest rozpoznawany po wielkości, która jest mniejsza od poprzednich (domyslnie musi mieścić się w przedziale od 0 bajtów do 511 bajtów)
\item jeżeli odpowiedź ACK dla danego segmentu nigdy nie została otrzymana przez maszynę wysyłającą, po upływie określonego czasu (timeout) segment jest retransmitowany
\end{itemize}
\paragraph{PXE}
PXE (Preboot eXecution Environment) to specyfikacja ustandaryzowanego środowiska pozwalającego na uruchamianie oprogramowania
poprzez sieć. Rozwiązanie takie pracuje w architekturze klient-serwer, gdzie od klienta wymagane jest jedynie posiadanie
odpowiedniej karty sieciowej (wspierającej to rozwiązanie) oraz wsparcie w oprogramowaniu inicjalizującym maszyny, odpowiedzialnym
za uruchamianie oprogramowania (na przykład BIOS/UEFI dla maszyn o architekturze X86). Część serwera jest zbiorem już wcześniej istniejących
rozwiązań, składającym się z serwera DHCP oraz serwera TFTP. \cite{bib:pxe-specs}
Proces uruchamiania składa się z następujących kroków\cite{bib:pxe-specs}:
\begin{itemize}
\item wysłanie zapytania \texttt{DHCPDISCOVER} w trybie BROADCAST przez klienta
\item serwer DHCP odpowiada poprzez wiadomość \texttt{DHCPOFFER}; jeżeli serwer DHCP nie implementuje obsługi PXE, wysyła poprawną wiadomość nie zawierającą informacji o lokalizacji plików potrzebnych do uruchomienia minimalnego systemu, a maszyna klienta nie uruchamia się poprzez PXE. W przeciwnym przypadku, serwer DHCP poza zwykłymi informacjami przesyłanymi we wiadomości \texttt{DHCPOFFER} dodaje także informacje o lokalizacji serwera TFTP, gdzie znajdują się pliki potrzebne do uruchomienia minimalnego środowiska uruchomienowego, oraz o nazwach tych plików
\item klient PXE konfiguruje się danymi przesłanymi przez serwer DHCP oraz pobiera pliki uruchomieniowe do pamięci RAM (Random Access Memory); uruchamia otrzymane pliki
\item minimalne środowisko uruchomieniowe powinno pobrać pełen obraz instalacyjny lub obraz systemu do uruchomienia i obsłużyć jego urchomienie/instalację; ten krok najczęściej jest wykonywany poprzez bardziej skomplikowane i niezawodne protokoły, takie jak HTTP, lub NFS
\end{itemize}
Jako minimalne środowisko uruchomieniowe wykorzystuje się często zestaw jądra Linux oraz prostego obrazu initrd, które mają za zadanie
pobrać i uruchomić instalator pełnego systemu operacyjnego.
%\begin{itemize}
%\item sformułowanie problemu
%\item osadzenie tematu w kontekście aktualnego stanu wiedzy (\ang{state of the art}) o poruszanym problemie
%\item studia literaturowe \cite{bib:artykul,bib:ksiazka,bib:konferencja,bib:internet} - opis znanych rozwiązań (także opisanych naukowo, jeżeli problem jest poruszany w publikacjach naukowych), algorytmów,
%\end{itemize}
%
%
% Wzory
% \begin{align}
% y = \frac{\partial x}{\partial t}
% \end{align}
% jak i pojedyncze symbole $x$ i $y$ składa sięw trybie matematycznym.
Do implementacji pierwszej części projektu wyszczególnionej powyżej wykorzystano język Python, do zaimplementowania prostego serwera oraz klienta, synchronizujących
obrazy maszyn wirtualnych, QEMU wraz z KVM do uruchamiania samych maszyn oraz system operacyjny nadzorcy Ubuntu Linux w wersji 22.04 LTS.
\paragraph{}
Część projektu odpowiedzialna za obsługę systemów zarządców maszyn wirtualnych została także oparta o system operacyjny Ubuntu Linux w wersji 22.04 LTS,
serwer TFTP oraz PXE do serwowania medium instalacyjnego systemu bazowego nadzorców, cloud-init oraz Ansible do automatyzacji instalacji i konfiguracji hostów.
\paragraph{QEMU}
QEMU to zintegrowany system do emulacji procesorów poprzez tłumaczenie binarne, potrafi także korzystać z technologii KVM do uruchamiania
aplikacji/maszyn wirtualnych z minimalnym narzutem wydajnościowym. \cite{bib:qemu-main}
Krótka wstawka kodu w linii tekstu jest możliwa, np. \lstinline|int a;| (biblioteka \texttt{listings})% lub \mintinline{C++}|int a;| (biblioteka \texttt{minted})
.
Dłuższe fragmenty lepiej jest umieszczać jako rysunek, np. kod na rys \ref{fig:pseudokod:listings}% i rys. \ref{fig:pseudokod:minted}