diff --git a/deployer/bootiful-common b/deployer/bootiful-common index a36c068f7108c8c3a6edb63812e0d8758e6848e2..1e29fc76f6370660b4fc31a75cc3892593df5fa3 100644 --- a/deployer/bootiful-common +++ b/deployer/bootiful-common @@ -418,8 +418,13 @@ ensure_mounted() { echo_err "Attempting to mount '$source_device' on '$mount_point' as '$mount_fstype' with options '$mount_options'." - mount -t "$mount_fstype" -o "$mount_options" "$source_device" "$mount_point" || - fatal_error "Failed to mount device '$source_device' on '$mount_point'". + if [[ -z "$mount_fstype" && -z "$mount_options" ]]; then + mount "$source_device" "$mount_point" || + fatal_error "Failed to mount device '$source_device' on '$mount_point'". + else + mount -t "$mount_fstype" -o "$mount_options" "$source_device" "$mount_point" || + fatal_error "Failed to mount device '$source_device' on '$mount_point'". + fi echo_err "Mount successful." } diff --git a/deployer/bootiful-deploy b/deployer/bootiful-deploy index e75871d937a59e2824cedd10f92644c0bcfee12b..e8d9f886badccdde5cd303e9c727266b8d3cacac 100755 --- a/deployer/bootiful-deploy +++ b/deployer/bootiful-deploy @@ -104,18 +104,18 @@ echo "Starting logging stdout and stderr to $log_file..." remote_images_dir="$mounting_point_remote/images" echo "Finding remote images..." - declare count=0 + declare images_count=0 declare -A images declare found_image_name for image_folder in "$remote_images_dir"/*; do found_image_name=$(basename "$image_folder") echo "Image '$found_image_name' found" - options=("${options[@]}" "$((++count))" "$found_image_name") - images[$count]="$found_image_name" + image_options=("${image_options[@]}" "$((++images_count))" "$found_image_name") + images[$images_count]="$found_image_name" done - echo "$count remote images found." + echo "$images_count remote images found." - if [[ $count -eq 0 ]]; then + if [[ $images_count -eq 0 ]]; then fatal_error "No image found in remote images directory $remote_images_dir" fi @@ -125,25 +125,26 @@ echo "Starting logging stdout and stderr to $log_file..." tty=$(tty) readonly tty - declare choice - choice=$(dialog \ + declare image_choice + image_choice=$(dialog \ --clear \ --title "Image selection" \ --menu "Select an image to deploy" \ 0 0 0 \ - "${options[@]}" \ + "${image_options[@]}" \ 2>&1 > "$tty") - readonly choice + readonly image_choice - validate_not_empty "$choice" "No image has been chosen" + validate_not_empty "$image_choice" "No image has been chosen" - readonly image_name=${images[$choice]} + readonly image_name=${images[$image_choice]} echo "Chosen image is $image_name" readonly remote_image_dir="$remote_images_dir/$image_name" readonly remote_image_gzip_file="$remote_image_dir/$image_name.img.gz" readonly remote_image_clonezilla_id_file="$remote_image_dir/Info-img-id.txt" + readonly remote_image_customizations_dir="$remote_image_dir/customizations" readonly IMAGE_TYPE_RAW="raw" readonly IMAGE_TYPE_CLONEZILLA="clonezilla" @@ -196,6 +197,33 @@ echo "Starting logging stdout and stderr to $log_file..." DEPLOY_MODE_CACHED_NOW='cached_now' DEPLOY_MODE_NOT_CACHED='not_cached' + if [[ -d "$remote_image_customizations_dir" ]]; then + start_step_batch "remote customizations search" + declare customizations_count=0 + declare -A customizations + declare found_customization_name + for customization_folder in "$remote_image_customizations_dir"/*; do + found_customization_name=$(basename "$customization_folder") + echo "Customization '$found_customization_name' found" + customization_options=("${customization_options[@]}" "$((++customizations_count))" "$found_customization_name" "off") + customizations[$customizations_count]="$found_customization_name" + done + echo "$customizations_count customizations found." + readonly customization_options + readonly customizations + + start_step_interactive "customizations selection" + declare customization_choices + customization_choices=$(dialog \ + --clear \ + --title "Customizations selection" \ + --checklist "Select customizations to apply after deployment" \ + 0 0 0 \ + "${customization_options[@]}" \ + 2>&1 > "$tty") + readonly customization_choices + fi + ensure_image_cache_partition_start if [[ "$image_size" -gt "$image_cache_partition_start" ]]; then start_step_interactive "hidden partition destruction confirmation" @@ -439,6 +467,24 @@ echo "Starting logging stdout and stderr to $log_file..." fsck -y "$hidden_partition_dev" fi + if [[ -n "$customization_choices" ]]; then + start_step_batch "customizations deployment" + + for customization_choice in $customization_choices; do + customization="${customizations[customization_choice]}" + customization_dir="$remote_image_customizations_dir/$customization" + + echo "Deploying customization '$customization' from '$customization_dir'" + for customization_partition in "$customization_dir"/*; do + validate_with_regex "$customization_partition" '^sda[0-9]$' "customization sub-directory name does not match a partition of sda" + customization_partition_mount_point="/bootiful/mounted_customization_partitions/$customization_partition" + customization_partition_device="/dev/$customization_partition" + ensure_mounted "$customization_partition_device" "$customization_partition_mount_point" + cp -RT "$customization_dir/$customization_partition/" + done + done + fi + start_step_batch "EFI entrypoint file creation" readonly remote_image_efi_entrypoint_file="$remote_image_dir/efi_entrypoint" diff --git a/doc/abstract.pdf b/doc/abstract.pdf index 68baf849bf3e99ee14489cc74425ff5be19b3ae6..33502f9d90f7d5f195659cef6e21cfa4797709dc 100644 Binary files a/doc/abstract.pdf and b/doc/abstract.pdf differ diff --git a/doc/abstract/abstract.tex b/doc/abstract/abstract.tex index 30eafebee76060a4b4749ddb17eaf86406c203a7..a36e0faeff31e217aa8fad5b78822f503e313bef 100644 --- a/doc/abstract/abstract.tex +++ b/doc/abstract/abstract.tex @@ -17,6 +17,7 @@ \input{config/conf} %\usepackage{showframe} % Prints document frame +\setlength{\parindent}{0pt} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DOCUMENT STARTS BELOW %%%%%%%%%%%%%%%%%%%%%%%%% @@ -30,7 +31,7 @@ \vspace{0.5cm} %% CONTENT STARTS HERE -Le sujet de ce travail de diplôme est la développement d’une solution de déploiement de systèmes d’exploitation à la +Le sujet de ce travail de diplôme est le développement d’une solution de déploiement de systèmes d’exploitation à la demande, pour une utilisation dans le cadre d’une école d’informatique. À chaque démarrage d’un poste de travail, l’utilisateur peut choisir quel système il souhaite utiliser dans une liste prédéfinie. Il est aussi possible de définir une liste de personnalisations qui permettent de configurer ce système, comme par exemple l’installation de certains @@ -42,12 +43,11 @@ qu’elle devra être installée. Une fois le déploiement terminé, les personn système choisi est démarré et est utilisable jusqu’à l’extinction de l’ordinateur. Au prochain démarrage, un nouveau système d’exploitation sera de nouveau déployé. Ce mode de fonctionnement permet aux élèves de l’école de toujours utiliser un système propre et spécifique à leurs besoins immédiats. Des efforts ont été menés pour faire en sorte -que les déploiements se fassent le plus rapidement possible, en économisant un maximum les resources disque et réseau, +que les déploiements se fassent le plus rapidement possible, en économisant un maximum les ressources disque et réseau, tout en fonctionnant avec des ordinateurs modernes. Le développement de ce projet étant destiné à être continué après ce travail de diplôme, un soin particulier a été apporté pour automatiser et simplifier au maximum la construction et la mise en fonctionnement du système dans un environnement réel. - \vfill \begin{center} {\includegraphics[]{../images/bootiful_logo_final.pdf}}\\* diff --git a/doc/diagrams/usecases.puml b/doc/diagrams/usecases.puml index 95715ea0a043ca1007479ac535485b9d6c3e66b5..71379882f8c25497d94f7a815d406d7dc5a1d7c3 100644 --- a/doc/diagrams/usecases.puml +++ b/doc/diagrams/usecases.puml @@ -18,7 +18,7 @@ rectangle "<$server{scale=.38}> Bootiful server" as server { :admin: - (add_os) :admin: - (add_config) -rectangle "<$client{scale=.38}> Client computer" as client { +rectangle "<$desktop{scale=.38}> Client computer" as client { usecase "Use a disposable operating system" as use_os usecase "Choose an operating system image" as choose_os diff --git a/doc/gitlab-markdown.gpp b/doc/gitlab-markdown.gpp index 69b76fe1decd933a904180427967c40a2023290c..6da6fc4e159417e56b970694ee671f287322a143 100644 --- a/doc/gitlab-markdown.gpp +++ b/doc/gitlab-markdown.gpp @@ -20,6 +20,10 @@ !!define{!!ref{ref_id}}{} +!!define{!!jpg{description}{filename}}{} + +!!define{!!jpgref{ref_id}{description}{filename}}{!!jpg{!!description\label{!!ref_id}}{!!filename}} + !!define{!!video{description}{filename}}{} !!define{!!tableofcontents}{ diff --git a/doc/images/diagram_usecases.png b/doc/images/diagram_usecases.png index df0580ef8714ca5a629a8c28bf06b29d04034455..ff607c512494420bcdeceb285c69e3101cb48a25 100644 --- a/doc/images/diagram_usecases.png +++ b/doc/images/diagram_usecases.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b1405375b114af7e2b6b96b2431a0f440d5856e2537dc702390651b0ba0403c -size 39914 +oid sha256:2d8e1206b69900442e8342ef40b39b93d11742b49fcdbd6b2b1fd120b075751a +size 39418 diff --git a/doc/images/diagram_usecases.svg b/doc/images/diagram_usecases.svg index f9314f3539d182c54a58a235162c4dcdcaf9d3c4..4ce172fef2495d1871bb5288f3ca0e6d9af6ba41 100644 --- a/doc/images/diagram_usecases.svg +++ b/doc/images/diagram_usecases.svg @@ -1,19 +1,62 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="487px" preserveAspectRatio="none" style="width:867px;height:487px;" version="1.1" viewBox="0 0 867 487" width="867px" zoomAndPan="magnify"><defs/><g><!--MD5=[4ef80439e1ef409f8f3a0ec422262701] -cluster server--><rect fill="#FFFFFF" height="255.5" style="stroke: #000000; stroke-width: 1.5;" width="306" x="124.5" y="213.5"/><image height="18" width="18" x="207.38" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAAtklEQVR42p2SMQqEMBBFRbAIaCnJAXIDaw9rGdJ5EYs0sTVYuyzElRjdHUEQtprJhwwE8jLzh599k5TB2ff9hdZ5njeWEeW9R2HzPFdV5Zxrmgau4ziisL7voWqthRAE7E+3t8RNUruFEFDYNE1QrbV1XRO8GWMYY8MwSCkfrCgK0pDbtl3YcRwftJ6UcM5J3WKMKG9KKahd1+V5TlgJvCnLEiZs2zY9JcuyXBh880ZrXdf0cP0A4A+hcONBrG4AAAAASUVORK5CYII=" y="215.5"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="117" x="230.62" y="230.4382">Bootiful server</text><!--MD5=[aa2f05b2676d7d34ffa7e771fb7634f3] -cluster client--><rect fill="#FFFFFF" height="444" style="stroke: #000000; stroke-width: 1.5;" width="329" x="470.5" y="32"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="126" x="574.5" y="46.9951">Client computer</text><ellipse cx="287.3655" cy="284.7731" fill="#FEFECE" rx="119.3655" ry="26.2731" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="218" x="178.3655" y="289.4215">Add an operating system image</text><ellipse cx="287.2901" cy="426.258" fill="#FEFECE" rx="79.2901" ry="18.258" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="129" x="222.7901" y="430.9065">Add configurations</text><ellipse cx="644.6751" cy="103.535" fill="#FEFECE" rx="130.6751" ry="28.535" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="242" x="523.6751" y="108.1835">Use a disposable operating system</text><ellipse cx="643.5975" cy="284.8195" fill="#FEFECE" rx="132.0975" ry="28.8195" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="245" x="521.0975" y="289.4679">Choose an operating system image</text><ellipse cx="643.5937" cy="426.6187" fill="#FEFECE" rx="116.0937" ry="25.6187" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="211" x="538.0937" y="431.2672">Choose a set of configurations</text><!--MD5=[79a439086b036a57b90a01d7b2f8bbd1] -entity admin--><ellipse cx="51.5" cy="255.5" fill="#FEFECE" rx="8" ry="8" style="stroke: #A80036; stroke-width: 1.5;"/><path d="M51.5,263.5 L51.5,290.5 M38.5,271.5 L64.5,271.5 M51.5,290.5 L38.5,305.5 M51.5,290.5 L64.5,305.5 " fill="none" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="91" x="6" y="320.9951">Administrator</text><!--MD5=[8fc3522a43f8c7199df5e09e5bb0188e] -entity user--><ellipse cx="839.5" cy="74" fill="#FEFECE" rx="8" ry="8" style="stroke: #A80036; stroke-width: 1.5;"/><path d="M839.5,82 L839.5,109 M826.5,90 L852.5,90 M839.5,109 L826.5,124 M839.5,109 L852.5,124 " fill="none" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="32" x="823.5" y="139.4951">User</text><!--MD5=[5c07b811968b028b77d5e4b5f347e66f] -reverse link add_os to add_config--><path d="M287.5,316.64 C287.5,344.5 287.5,384.66 287.5,407.61 " fill="none" id="add_os<-add_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="287.5,311.59,283.5,320.59,287.5,316.59,291.5,320.59,287.5,311.59" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="60" x="288.5" y="367.0669">«extend»</text><!--MD5=[4f3fd4ebd51b7c0368d82b3e532c8b9a] -link admin to add_os--><path d="M63.97,245.93 C78.83,207.22 106.8,155.64 147,170 C196.28,187.6 240.72,230.73 265.89,258.88 " fill="none" id="admin-add_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[6287d5951c886eba17f455b906a73239] -link admin to add_config--><path d="M97,312.9 C145.09,341.32 219.4,385.24 259.52,408.96 " fill="none" id="admin-add_config" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[151ad9e7e426fb4f768450ddac30b0ef] -link use_os to choose_os--><path d="M644.35,132.15 C644.17,163.99 643.88,216.26 643.69,250.4 " fill="none" id="use_os->choose_os" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="643.66,255.72,647.7114,246.743,643.6886,250.7201,639.7115,246.6973,643.66,255.72" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="61" x="644.5" y="185.5669">«include»</text><!--MD5=[2eb808ac3e1d0466e9cafd7264bda8dc] -reverse link choose_os to choose_config--><path d="M643.5,319.42 C643.5,344.38 643.5,377.95 643.5,400.59 " fill="none" id="choose_os<-choose_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="643.5,314.23,639.5,323.23,643.5,319.23,647.5,323.23,643.5,314.23" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="60" x="644.5" y="367.0669">«extend»</text><!--MD5=[6e668341980b2b6f3174a2b130bbe9df] +<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="464px" preserveAspectRatio="none" style="width:867px;height:464px;" version="1.1" viewBox="0 0 867 464" width="867px" zoomAndPan="magnify"><defs/><g><!--MD5=[4ef80439e1ef409f8f3a0ec422262701] +cluster server--><rect fill="#FFFFFF" height="255.5" style="stroke: #000000; stroke-width: 1.5;" width="306" x="124.5" y="190.5"/><image height="18" width="18" x="207.38" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAAtklEQVR42p2SMQqEMBBFRbAIaCnJAXIDaw9rGdJ5EYs0sTVYuyzElRjdHUEQtprJhwwE8jLzh599k5TB2ff9hdZ5njeWEeW9R2HzPFdV5Zxrmgau4ziisL7voWqthRAE7E+3t8RNUruFEFDYNE1QrbV1XRO8GWMYY8MwSCkfrCgK0pDbtl3YcRwftJ6UcM5J3WKMKG9KKahd1+V5TlgJvCnLEiZs2zY9JcuyXBh880ZrXdf0cP0A4A+hcONBrG4AAAAASUVORK5CYII=" y="192.5"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="117" x="230.62" y="207.4382">Bootiful server</text><!--MD5=[aa2f05b2676d7d34ffa7e771fb7634f3] +cluster client--><rect fill="#FFFFFF" height="446" style="stroke: #000000; stroke-width: 1.5;" width="329" x="470.5" y="7"/><image height="18" width="18" x="560.38" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAAgElEQVR42mP4TxZgAOIfP35cJBr8/fsXqo2BRADUCdXGycmZSwTQ1tYGKr558yZUm4iICDFeSkxMHNU2mLSRBF69egXSduTIEQtUAEkNQCAhIYEmtXXrVmiaxAROTk4QbYqKijhzABzMnDkTl8MSEhJwavv9+/drHAAohVMb8QAAzuOcd9U0ThYAAAAASUVORK5CYII=" y="9"/><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="126" x="583.62" y="23.9382">Client computer</text><ellipse cx="287.3655" cy="261.7731" fill="#FEFECE" rx="119.3655" ry="26.2731" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="218" x="178.3655" y="266.4215">Add an operating system image</text><ellipse cx="287.2901" cy="403.258" fill="#FEFECE" rx="79.2901" ry="18.258" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="129" x="222.7901" y="407.9065">Add configurations</text><ellipse cx="644.6751" cy="80.535" fill="#FEFECE" rx="130.6751" ry="28.535" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="242" x="523.6751" y="85.1835">Use a disposable operating system</text><ellipse cx="643.5975" cy="261.8195" fill="#FEFECE" rx="132.0975" ry="28.8195" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="245" x="521.0975" y="266.4679">Choose an operating system image</text><ellipse cx="643.5937" cy="403.6187" fill="#FEFECE" rx="116.0937" ry="25.6187" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="211" x="538.0937" y="408.2672">Choose a set of configurations</text><!--MD5=[79a439086b036a57b90a01d7b2f8bbd1] +entity admin--><ellipse cx="51.5" cy="232.5" fill="#FEFECE" rx="8" ry="8" style="stroke: #A80036; stroke-width: 1.5;"/><path d="M51.5,240.5 L51.5,267.5 M38.5,248.5 L64.5,248.5 M51.5,267.5 L38.5,282.5 M51.5,267.5 L64.5,282.5 " fill="none" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="91" x="6" y="297.9951">Administrator</text><!--MD5=[8fc3522a43f8c7199df5e09e5bb0188e] +entity user--><ellipse cx="839.5" cy="51" fill="#FEFECE" rx="8" ry="8" style="stroke: #A80036; stroke-width: 1.5;"/><path d="M839.5,59 L839.5,86 M826.5,67 L852.5,67 M839.5,86 L826.5,101 M839.5,86 L852.5,101 " fill="none" style="stroke: #A80036; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="32" x="823.5" y="116.4951">User</text><!--MD5=[5c07b811968b028b77d5e4b5f347e66f] +reverse link add_os to add_config--><path d="M287.5,293.64 C287.5,321.5 287.5,361.66 287.5,384.61 " fill="none" id="add_os<-add_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="287.5,288.59,283.5,297.59,287.5,293.59,291.5,297.59,287.5,288.59" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="44" x="288.5" y="344.0669">extend</text><!--MD5=[4f3fd4ebd51b7c0368d82b3e532c8b9a] +link admin to add_os--><path d="M63.97,222.93 C78.83,184.22 106.8,132.64 147,147 C196.28,164.6 240.72,207.73 265.89,235.88 " fill="none" id="admin-add_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[6287d5951c886eba17f455b906a73239] +link admin to add_config--><path d="M97,289.9 C145.09,318.32 219.4,362.24 259.52,385.96 " fill="none" id="admin-add_config" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[151ad9e7e426fb4f768450ddac30b0ef] +link use_os to choose_os--><path d="M644.35,109.15 C644.17,140.99 643.88,193.26 643.69,227.4 " fill="none" id="use_os->choose_os" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="643.66,232.72,647.7114,223.743,643.6886,227.7201,639.7115,223.6973,643.66,232.72" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="45" x="644.5" y="162.5669">include</text><!--MD5=[2eb808ac3e1d0466e9cafd7264bda8dc] +reverse link choose_os to choose_config--><path d="M643.5,296.42 C643.5,321.38 643.5,354.95 643.5,377.59 " fill="none" id="choose_os<-choose_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="643.5,291.23,639.5,300.23,643.5,296.23,647.5,300.23,643.5,291.23" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="44" x="644.5" y="344.0669">extend</text><!--MD5=[6e668341980b2b6f3174a2b130bbe9df] link client to server--><!--MD5=[d04cbd9972015fff3c60bc8b91edbf3c] -link user to use_os--><path d="M823.41,103.5 C807.32,103.5 791.23,103.5 775.13,103.5 " fill="none" id="user-use_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[5ac92743f61ff4c80136b7efe96e452e] -link user to choose_os--><path d="M823.46,119.19 C790.64,149.25 715.37,218.18 673.31,256.7 " fill="none" id="user-choose_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[05684dfad36a9b1dec96054d56bd6a76] -link user to choose_config--><path d="M839.93,142.74 C838.67,189.36 830.61,268.72 793.5,324 C769.4,359.9 727.92,386.51 694.68,403.46 " fill="none" id="user-choose_config" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[2e19f14ccde8cbbedf6dedaa7937821a] -link add_os to choose_os--><path d="M407.03,285 C438.86,285 473.5,285 506.19,285 " fill="none" id="add_os->choose_os" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="511.42,285,502.42,281,506.42,285,502.42,289,511.42,285" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="60" x="429.25" y="278.0669">«extend»</text><!--MD5=[e430beec519377eab46017d19e346244] -link add_config to choose_config--><path d="M367.39,426.5 C412.98,426.5 471.05,426.5 522.16,426.5 " fill="none" id="add_config->choose_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="527.4,426.5,518.4,422.5,522.4,426.5,518.4,430.5,527.4,426.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="60" x="417.25" y="419.5669">«extend»</text><!--MD5=[5c161203b934efde5361f6eb1d0d5481] +link user to use_os--><path d="M823.41,80.5 C807.32,80.5 791.23,80.5 775.13,80.5 " fill="none" id="user-use_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[5ac92743f61ff4c80136b7efe96e452e] +link user to choose_os--><path d="M823.46,96.19 C790.64,126.25 715.37,195.18 673.31,233.7 " fill="none" id="user-choose_os" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[05684dfad36a9b1dec96054d56bd6a76] +link user to choose_config--><path d="M839.93,119.74 C838.67,166.36 830.61,245.72 793.5,301 C769.4,336.9 727.92,363.51 694.68,380.46 " fill="none" id="user-choose_config" style="stroke: #A80036; stroke-width: 1.0;"/><!--MD5=[2e19f14ccde8cbbedf6dedaa7937821a] +link add_os to choose_os--><path d="M407.03,262 C438.86,262 473.5,262 506.19,262 " fill="none" id="add_os->choose_os" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="511.42,262,502.42,258,506.42,262,502.42,266,511.42,262" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="44" x="437.25" y="255.0669">extend</text><!--MD5=[e430beec519377eab46017d19e346244] +link add_config to choose_config--><path d="M367.39,403.5 C412.98,403.5 471.05,403.5 522.16,403.5 " fill="none" id="add_config->choose_config" style="stroke: #A80036; stroke-width: 1.0; stroke-dasharray: 7.0,7.0;"/><polygon fill="#A80036" points="527.4,403.5,518.4,399.5,522.4,403.5,518.4,407.5,527.4,403.5" style="stroke: #A80036; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="44" x="425.25" y="396.5669">extend</text><!--MD5=[7d5ac7c9ea14185df274f76d3d8afd1a] +@startuml +!include <tupadr3/common> +!include <tupadr3/font-awesome-5/server> +!include <tupadr3/font-awesome-5/desktop> + +skinparam style strictuml + +actor "Administrator" as admin +actor "User" as user + +rectangle "<$server{scale=.38}> Bootiful server" as server { + usecase "Add an operating system image" as add_os + usecase "Add configurations" as add_config + + add_config .up.> add_os: "«extend»" +} + +:admin: - (add_os) +:admin: - (add_config) + +rectangle "<$desktop{scale=.38}> Client computer" as client { + usecase "Use a disposable operating system" as use_os + + usecase "Choose an operating system image" as choose_os + usecase "Choose a set of configurations" as choose_config + + (use_os) ..> (choose_os): "«include»" + (choose_config) .up.> (choose_os): "«extend»" +} + + +server -[hidden]up- client + + +:user: - (use_os) +:user: - (choose_os) +:user: - (choose_config) + +(add_os) .> (choose_os): "«extend»" +(add_config) .> (choose_config): "«extend»" + +@enduml + @startuml @@ -140,7 +183,7 @@ rectangle "<$server{scale=.38}> Bootiful server" as server { :admin: - (add_os) :admin: - (add_config) -rectangle "<$client{scale=.38}> Client computer" as client { +rectangle "<$desktop{scale=.38}> Client computer" as client { usecase "Use a disposable operating system" as use_os usecase "Choose an operating system image" as choose_os @@ -163,12 +206,10 @@ server -[hidden]up- client @enduml -PlantUML version 1.2020.07(Sun Apr 19 13:42:40 CEST 2020) +PlantUML version 1.2020.15(Sun Jun 28 13:39:45 CEST 2020) (GPL source distribution) Java Runtime: OpenJDK Runtime Environment JVM: OpenJDK 64-Bit Server VM -Java Version: 1.8.0_252-b09 -Operating System: Linux Default Encoding: UTF-8 Language: en Country: US diff --git a/doc/images/mini_pc.jpg b/doc/images/mini_pc.jpg new file mode 100644 index 0000000000000000000000000000000000000000..76910163fefb52a4e9e5335e07d87fa73958119f Binary files /dev/null and b/doc/images/mini_pc.jpg differ diff --git a/doc/pandoc-pdf-markdown.gpp b/doc/pandoc-pdf-markdown.gpp index dd5fc5b3af75d37011b8dc750b135ed271ca4002..552076718ec401c17368ca764a5621e7a8afde2c 100644 --- a/doc/pandoc-pdf-markdown.gpp +++ b/doc/pandoc-pdf-markdown.gpp @@ -6,6 +6,10 @@ !!define{!!pdfref{ref_id}{description}{filename}}{!!pdf{!!description\label{!!ref_id}}{!!filename}} +!!define{!!jpg{description}{filename}}{} + +!!define{!!jpgref{ref_id}{description}{filename}}{!!jpg{!!description\label{!!ref_id}}{!!filename}} + !!define{!!label{ref_id}}{{#!!ref_id}} !!define{!!ref{ref_id}}{\ref{!!ref_id}} diff --git a/doc/rapport.gpp.md b/doc/rapport.gpp.md index abc2931c0973a5b645f63fbf4d788b42e379482a..75526fb5928fadb451fbb10be217a27268e90cc1 100644 --- a/doc/rapport.gpp.md +++ b/doc/rapport.gpp.md @@ -52,6 +52,8 @@ header-includes: | \setlength{\beforechapskip}{-10pt} \OnehalfSpacing + + \uchyph=0 --- !!comment{ ``` @@ -141,15 +143,17 @@ système d’exploitation (!!acronym{OS}) et aussi avoir la possibilité d’être administrateur sur la machine physique. En effet, utiliser une machine virtuelle n’est pas toujours souhaitable, surtout lorsqu’il y a interaction avec du matériel, typiquement lors du développement pour -l’embarqué. Le but de ce projet est de développer un système de -déploiement répondant à ces besoins. Lors de la mise sous tension d’une -machine, le système proposerait à l’utilisateur un choix d’OSes et -celui-ci s’installerait à la volée (distributions Linux, différentes -versions de Windows, etc.). Afin de minimiser le temps d’attente lors de +l’embarqué. + +Le but de ce projet est de développer un système de déploiement +répondant à ces besoins. Lors de la mise sous tension d’une machine, le +système proposerait à l’utilisateur un choix d’OSes et celui-ci +s’installerait à la volée (distributions Linux, différentes versions de +Windows, etc.). Afin de minimiser le temps d’attente lors de l’installation de l’!!acronym{OS}, un mécanisme de cache évitant le téléchargement des images préalablement téléchargées sera mis en place. On désire aussi que le système de déploiement permette de customiser -certains aspects de l’image choisie. Ce travail ce base sur une ébauche +certains aspects de l’image choisie. Ce travail se base sur une ébauche d’un travail déjà existant. ## Objectifs @@ -179,11 +183,11 @@ Les objectifs du projet, tirés de l´énoncé de ce dernier ## Déroulement -Ce projet n'a pas n'a pas été réalisé dans les locaux d'!!acronym{HEPIA} -mais à distance, depuis la maison, en partie à cause de la situation -sanitaire due au COVID-19 mais aussi parce qu'il est effectué dans le -cadre d'un bachelor en cours du soir, en parallèle à une activité -professionnelle à 80%. +Ce projet n'a pas été réalisé dans les locaux d'!!acronym{HEPIA} mais à +distance, depuis la maison, en partie à cause de la situation sanitaire +due au COVID-19 mais aussi parce qu'il est effectué dans le cadre d'un +bachelor en cours du soir, en parallèle à une activité professionnelle à +80%. Des rendez-vous hebdomadaires avec le professeur responsable Florent Glück ainsi que l'assistant Sébastien Chassot ont été organisés tous les @@ -191,10 +195,11 @@ mercredis, afin d'assurer un suivi du projet. ## Matériel à disposition -Deux mini-ordinateurs ont étés prêtés par !!acronym{HEPIA} pour la durée +Deux mini-ordinateurs ont été prêtés par !!acronym{HEPIA} pour la durée du projet, afin d'effectuer des tests sur du matériel réel. Les spécifications de ces ordinateurs sont définies dans la table -!!ref{mini_pc_specs}. +!!ref{mini_pc_specs}. Un photographie d'un ordinateur de ce type est +visible dans la figure !!ref{mini_pc_photo}. !!tableheader{mini_pc_specs}{Spécifications techniques des ordinateurs mis à disposition} @@ -202,11 +207,12 @@ spécifications de ces ordinateurs sont définies dans la table |:----------------|:--------------------------------------------------------------------| | Constructeur | _DELL_ | | Modèle | _Optiplex 7060 micro_ | -| Processeur | _Intel® Core™ i7-8700 @ 3.20GHz_ | -| Mémoire vive | 8 GiB (2 barettes 4GiB _SODIMM DDR4 Synchronous_ 2666 MHz (0.4 ns)) | +| Processeur | _Intel Core i7-8700_ 3.2GHz | +| Mémoire vive | 8 GiB (2×4GiB _SODIMM DDR4 Synchronous_ 2666 MHz (0.4 ns)) | | Disque | _TOSHIBA KSG60ZMV_ SSD 476GiB (512GB) | | Réseau | _Intel e1000e Gigabit Ethernet_ | +!!jpgref{mini_pc_photo}{Photo d'un ordinateur _DELL Optiplex 7060 micro_}{images/mini_pc} ## Méthodologie @@ -233,8 +239,8 @@ mais aucun d'entre eux ne répond précisément aux objectifs précis du cahier des charges de ce projet. Les sous-sections qui suivent décrivent quelques-uns de ces systèmes, -leurs points forts et points faibles et pourquoi ils ne sont pas adaptés -tels-quel pour ce projet. +leurs points forts et points faibles ainsi que pourquoi ils ne sont pas +adaptés tels quels pour ce projet. ### BpBatch / Rembo / !!acronym{IBM} Tivoli @@ -260,12 +266,12 @@ projet a été intégré à leur solution _Tivoli Provisionning Manager_ Ce système répond aux besoins de !!acronym{HEPIA}, et il a même déjà été utilisé au sein de l'institution par le passé. Il a cependant été -abandonné à cause de sa licence devenue très couteuse et de sa -complexité devenue trop grande pour les besoins simple de l'école. +abandonné à cause de sa licence devenue très coûteuse et de sa +complexité devenue trop grande pour les besoins simples de l'école. Le développement d'une solution alternative _open-source_ plus simple et -se basant sur des briques logicielles existantes serait donc très -intéressante. +se basant sur des briques logicielles existantes est donc +particulièrement intéressante. ### !!acronym{FOG} project @@ -274,25 +280,25 @@ déployer des images de systèmes d'exploitation sur des postes clients. !!cite{fog_wiki_intro} Elle fonctionne avec une architecture client-serveur. Le serveur est -accédé par une interface web, où un administrateur peut gérer ses images -et les déployer sur des clients qui démarrent un logiciel de déploiement -avec !!acronym{PXE}. +accédé par une interface web, dans laquelle un administrateur peut gérer +ses images et les déployer sur des clients qui démarrent un logiciel de +déploiement avec !!acronym{PXE}. -Cette solution est utilisées dans certaines classes à !!acronym{HEPIA} -pour déployer des images sur certains postes, notemment pour les travaux +Cette solution est utilisée dans certaines classes à !!acronym{HEPIA} +pour déployer des images sur certains postes, notamment pour les travaux de laboratoire de réseau. Malheureusement, ce système ne répond pas exactement au cahier des charges de ce projet: il est plutôt conçu pour "pousser" des images sur des clients, depuis le serveur, après une initiation du déploiement par un administrateur. Nous souhaitons plutôt un système ne nécessitant pas -d'intervention d'un administrateur pour initier un déploiement. +d'intervention d'un administrateur. -De plus,!!acronym{FOG} ne dispose pas de mécanisme de cache sur les +De plus, !!acronym{FOG} ne dispose pas de mécanisme de cache sur les clients permettant de ne pas télécharger les images plusieurs fois. À -chaque fois qu'un image doit être déployée, elle doit être téléchargée à -nouveau depuis le serveur, ce qui peut prendre beaucoup de temps et doit -être planifié à l'avance. +chaque fois qu'une image doit être déployée, elle doit être téléchargée +à nouveau depuis le serveur, ce qui peut prendre beaucoup de temps et +doit être planifié à l'avance. ### Clonezilla @@ -305,12 +311,13 @@ Cet outil peut être utilisé de deux manières: système d'exploitation de déploiement depuis un un serveur !!acronym{PXE}. Ils peuvent ensuite initier le déploiement ou la restauration d'une image depuis/vers ce serveur. -- En faisant démarrer les clients sur un CD ou une clé USB. Ils peuvent - ensuite initier le déploiement ou la restauration d'une image - d'!!acronym{OS} depuis/vers sur un autre disque ou un serveur distant. +- En faisant démarrer les clients sur un CD ou une clé USB. Ils est + ensuite possible d'initier le déploiement ou la restauration d'une + image d'!!acronym{OS} depuis/vers sur un autre disque ou un serveur + distant. Un avantage de _Clonezilla_ est le format utilisé pour la sauvegarde des -images. L'outil _Partclone_, développé par la même équipe, est utilisé. +images. L'outil utilisé, _Partclone_ est développé par la même équipe. Il permet de créer des images de partitions en ne copiant que les blocs utilisés dans le système de fichiers. Les parties non utilisées de la partition ne sont pas comprises dans l'image créée, ce qui permet de @@ -321,10 +328,10 @@ Malheureusement, comme pour !!acronym{FOG}, _Clonezilla_ ne dispose pas de mécanisme de cache sur les clients permettant de ne pas télécharger les images depuis un serveur distant plusieurs fois. À chaque fois qu'un image doit être déployée, elle doit être téléchargée à nouveau depuis le -serveur, ce qui peut prendre beaucoup de temps et doit être planifié à -l'avance. +serveur. Cette action peut prendre beaucoup de temps et doit être +planifiée à l'avance. -_Clonezilla_ ne peut donc pas être utilisé tel-quel. Cependant, il est +_Clonezilla_ ne peut donc pas être utilisé tel quel. Cependant, il est possible de l'utiliser uniquement pour la création d'images et d'intégrer son mécanisme de restauration à un autre système de déploiement. Cette possibilité sera étudiée plus tard dans ce document. @@ -375,20 +382,20 @@ addressable du disque, se trouve le secteur d'amorçage au format !!acronym{MBR}. Ce premier secteur contient deux informations nécessaires pour le chargement d'un système d'exploitation: -1. **La routine d'amorcage**: c'est le premier programme exécuté lors du - démarrage sur le disque. Sa taille est très limitée: 440 octets. Il - sert généralement à initier un chargeur d'amorcage ou _bootloader_ - présent un peu plus loin sur le disque, qui est un petit programme - qui va initier un système d'exploitation. +1. **La routine d'amorcage**: il s'agit du premier programme exécuté + lors du démarrage sur le disque. Sa taille est très limitée: 440 + octets. Il sert généralement à initier un chargeur d'amorcage ou + _bootloader_ présent un peu plus loin sur le disque, qui est un petit + programme qui va initier un système d'exploitation. 2. **La table des partitions**: définit une liste de partitions, qui sont des subdivisions logiques de l'espace du disque. Selon le système d'exploitation installé et la manière dont il a été configuré - lors de son installation initiale les partitions peuvent varier en + lors de son installation initiale, les partitions peuvent varier en nombre et en taille. Les données à l'intérieur de chaque partition sont généralement structurées en utilisant un système de fichiers. - Les partitions vont varier en nombre, taille, et format selon l'image - déployée. + Les partitions vont différer en nombre, taille, et format selon + l'image déployée. La seconde partie du disque est l'espace dédié à la mise en cache d'images d'!!acronym{OS}, qui prend 20% de l'espace total disponible, @@ -416,7 +423,7 @@ Enfin, les 4096 octets restants à la fin du disque sont dédiés à l'écriture d'une signature servant à signaler si une image vient d'être deployée sur le disque. La présence cette signature est verifiée à chaque démarrage pour déterminer s'il faut démarrer une image -fraichement déployée ou si au contraire l'image déployée a déjà été +fraîchement déployée ou si au contraire l'image déployée a déjà été utilisée et qu'il faut lancer un nouveau déploiement. Ainsi, le système peut s'assurer qu'une image disque déployée n'est exécutée qu'une seule fois, ce qui laisse la liberté à l'utilisateur du poste client de faire @@ -425,7 +432,7 @@ système inutilisable, car il suffit de redémarrer la machine pour repartir sur une base propre. Le mécanisme de signature est nécessaire à cause du moyen choisi pour -déployer les images: un système d'exploitation Linux minimaliste est est +déployer les images: un système d'exploitation Linux minimaliste est chargé en mémoire depuis le réseau afin de lancer le processus de choix d'une image et de la déployer. Une fois le déploiement terminé, la machine redémarre. La signature à la fin du disque est un moyen simple @@ -434,7 +441,7 @@ déployée et de la vérifier au démarrage. ### Serveur -Le composant central du système est un *serveur* linux qui inter-agit +Le composant central du système est un *serveur* linux qui interagit avec les postes clients pour leur permettre d'effectuer plusieurs actions: @@ -474,7 +481,7 @@ d'effectuer ces actions: du fichier exécutable du chargeur d'amorçage à récupérer sur ce dernier -- !!acronym{TFTP}: permet au clients de télécharger: +- !!acronym{TFTP}: permet aux clients de télécharger: - le fichier exécutable du chargeur d'amorçage - la configuration du chargeur d'amorçage - les modules optionels du chargeur d'amorçage, téléchargés selon la @@ -500,9 +507,6 @@ différents composants et leurs interactions. ## Processus de déploiement initial -**TODO: décrire pas à pas les différentes étapes du processus de -déploiement telles que montrées sur le diagramme.** - Les différentes étapes du processus de déploiement d'images sont décrites dans la figure !!ref{diagram_activity_deployment} qui est un diagrame d'activité détaillant les différentes étapes du processus de déploiement @@ -536,13 +540,14 @@ connecter à deux réseaux locaux distincts. Le serveur étant une machine virtuelle _VirtualBox_ créée par l'outil _Vagrant_, ses interfaces réseau sont aussi virtuelles. Ces interfaces -virtuelles ont été ratachées aux interfaces de la machine hôte de la +virtuelles ont été rattachées aux interfaces de la machine hôte de la manière suivante: -1. L'interface `eth0` de la machine virtuelle est ratachée à l'interface - `wlan0` de la machine hôte en utilisant le mode !!acronym{NAT}. -2. L'interface `eth1` de la machine virtuelle est ratachée à l'interface - `eth0` de la machine hôte en utilisant le mode `bridged`. +1. L'interface `eth0` de la machine virtuelle est rattachée à + l'interface `wlan0` de la machine hôte en utilisant le mode + !!acronym{NAT}. +2. L'interface `eth1` de la machine virtuelle est rattachée à + l'interface `eth0` de la machine hôte en utilisant le mode `bridged`. La figure !!ref{diagram_home_network} illustre les détails de la configuration réseau décrite ci-dessus. @@ -592,9 +597,9 @@ d'intervention possible. Le programme _Vagrant_ a été utilisé dans un premier temps pour automatiser la création d'une machine virtuelle à partir d'un fichier `Vagrantfile` définissant la configuration de la machine et les commandes à lancer pour l'installer et la configurer. Une -fois ce fichier de configuration créé, une seule commande est nécessaire -pour créer, configurer et lancer un serveur fonctionnel dans une machine -virtuelle: `vagrant up`. +fois ce fichier de configuration produit, une seule commande est +nécessaire pour créer, configurer et lancer un serveur fonctionnel dans +une machine virtuelle: `vagrant up`. !!svg{Logo du logiciel _Vagrant_ utilisé pour automatiser la création du serveur du système initial.}{images/vagrant_logo} @@ -617,8 +622,8 @@ il comporte de nombreuses limitations auxquelles il faudrait palier: commande, ce n'est pas très facile à utiliser. - Les scripts de déploiement sont fragiles et gèrent mal les erreurs. - Il n'y a pas de système de personnalisation d'image. Si on veut faire - !!inlinemath{n} personnalisation partant sur la base du même - !!acronym{OS}, il faut faire !!inlinemath{n} images: une pour chaque + !!inlinemath{n} personnalisation(s) partant sur la base du même + !!acronym{OS}, il faut faire !!inlinemath{n} image(s): une pour chaque personnalisation. - L'installation de nouveaux outils sur le système d'exploitation de l'!!acronym{OS} de déploiement créé avec _Buildroot_ peut être @@ -693,8 +698,8 @@ cette approche est qu'il est difficile de mettre à jour !!acronym{GRUB} sur une nouvelle version. Une nouvelle approche a été choisie pour intégrer le module `isign` à -!!acronym{GRUB}, qui permet de mettre à jour plus facilement -!!acronym{GRUB}, qui a d'ailleurs été utilisé en version 2.04, la +!!acronym{GRUB}, qui permet de mettre à jour plus facilement ce +programme, qui a d'ailleurs été mis à jour à la version 2.04 qui est la dernière version sortie au moment où cela a été fait. La nouvelle approche est la suivante: @@ -710,13 +715,6 @@ L'avantage de cette nouvelle approche est qu'il est maintenant facile d'intégrer les changements survenus dans le dépôt git officiel sur son clone pour bénéficier des dernières mises à jour de !!acronym{GRUB}. - - -**TODO: expliquer les problèmes rencontrés sans !!acronym{EFI} avec -lesmachines récentes, des modifications nécessaires pour supporter ce -nouveau système et des limitations que cela apporte.** - - ## Amélioration de la vitesse de transfert des images Le protocole !!acronym{NFS} est utilisé dans le système initial pour @@ -753,13 +751,13 @@ Les différents protocoles qui ont été mesurés sont: plus rapide, car permet de faire plus qu'un simple transfert de fichiers. - !!acronym{SMB}: le protocole standard pour monter à distance des - dossier partagés entre des !!acronym{OS} Windows. Une implémentation - existe sous linux, _Samba_, et elle est courremment utilisée pour une - meilleure compatiblité avec les systèmes Windows que sont équivalent + dossiers partagés entre des !!acronym{OS} Windows. Une implémentation + existe sous linux, _Samba_, et elle est couramment utilisée pour une + meilleure compatibilité avec les systèmes Windows que son équivalent !!acronym{NFS}. - !!acronym{IPFS}: ce protocole pair à pair décentralisé permet à chacun - de des pairs de mettre à disposition ou de télécharger des fichiers. - Il pourrait être intéressant d'utiliser ce protocole pour soulager la + des pairs de mettre à disposition ou de télécharger des fichiers. Il + pourrait être intéressant d'utiliser ce protocole pour soulager la charge du serveur, car tous les clients connectés peuvent se partager des parties d'images qu'ils ont déjà. Le serveur de déploiement ne serait qu'un pair de plus, qui dispose de toutes les images. Il est @@ -1149,7 +1147,7 @@ est: `QmX9MYUQhjKxua6HQMtpzaZd9ui4gGT75FJgAxeQJC47Ei` -On peutmaintenant le rajouter dans la configuration des noeuds +On peut maintenant le rajouter dans la configuration des noeuds d'amorçage des deux machines en lançant la commande suivante sur chacune d'entre elle: @@ -1157,7 +1155,7 @@ d'entre elle: IPFS_PATH=~/.ipfs ipfs bootstrap add /dnsaddr/debian1.home/p2p/QmX9MYUQhjKxua6HQMtpzaZd9ui4gGT75FJgAxeQJC47Ei ``` -Maintenant, le _daemon_ !!acronym{IPFS} peut être lancé sur chacune des +À présent, le _daemon_ !!acronym{IPFS} peut être lancé sur chacune des machines. La variable d'environnement `LIBP2P_FORCE_PNET=1` est définie pour forcer les échanges à se faire sur un réseau privé. @@ -1171,8 +1169,8 @@ terminal sera ouvert pour la suite des commandes. Dans une configuration plus durable, il faudrait le faire tourner en arrière plan, par exemple avec une unité systemd. -Maintenant on peut partager l'image sur le réseau avec la commande -suivante sur `debian1`: +On peut enfin partager l'image sur le réseau avec la commande suivante +sur `debian1`: ```bash $ IPFS_PATH=~/.ipfs ipfs add win10.tar.gz @@ -1184,7 +1182,7 @@ Le temps d'ajout de l'image a pris plus de trois minutes. À la fin, la référence du fichier, qui l'identifie de manière unique sur le réseau, est affichée: `QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv`. -On peut maintenant tenter de récupérer le fichier depuis `debian2`: +On peut alors tenter de récupérer le fichier depuis `debian2`: ```bash $ IPFS_PATH=~/.ipfs ipfs get QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv @@ -1192,9 +1190,9 @@ Saving file(s) to QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv 12.80 GiB / 12.80 GiB [==============================================================================] 100.00% 7m27s ``` -Le transfert a pris !!inlinemath{7m27s = 447s}. C'est beaucoup trop long -pour nos besoins. Le benchmark avec `perf` n'a même pas été effectué au -vu de ce résultat. +Le transfert a pris !!inlinemath{7m27s = 447s}. Ce résultat est beaucoup +trop long pour nos besoins. Le benchmark avec `perf` n'a même pas été +effectué au vu de ce résultat. ### Choix du protocole de transfert de fichiers @@ -1202,13 +1200,13 @@ La table !!ref{table_protocols} ainsi que la figure !!ref{chart_protocols} récapitulent les résultats mesurés pour chacun des protocoles testés. On peut remarquer que pour tous les protocoles, sauf !!acronym{IPFS}, les résultats sont proches de 120 secondes. Cette -durée montre que pour ces protocoles, on est proches de la limite +durée montre que pour ces protocoles, on se rapproche de la limite théorique de gigabit ethernet: !!displaymath{\frac{13743592907 [B]}{120 [s]} = \frac{109948743256 [b]}{120 [s]} \approx 916239527 [b/s] \approx 0.916 [Gb/s] \approx 1 [Gb/s]} On peut donc conclure que le choix du protocole !!acronym{NFS} est -approprié car les autres protocole n'accélèrent pas considérablement la +approprié car les autres protocoles n'accélèrent pas considérablement la vitesse de transfert d'une image. !!clearpage @@ -1246,7 +1244,7 @@ l'image et seuls les blocs utilisés sont ensuite restaurés. Les images contentant uniquement les blocs utilisés sont ensuite compressées avec `gzip`. -La taille des images créées avec clonezilla a été comparée à la taille +La taille des images créées avec _Clonezilla_ a été comparée à la taille des images déployées et à la taille des images juste compressées avec `gzip`, avec trois images: @@ -1303,15 +1301,15 @@ générer des systèmes embarqués. Il avait été choisi dans le système initial car il permettait de créer une image d'!!acronym{OS} très petite qui peut être téléchargée très rapidement sur le réseau. -### Problèmes avec_Buildroot_ pour construire l'!!acronym{OS} de déploiement +### Problèmes avec _Buildroot_ pour construire l'!!acronym{OS} de déploiement Buildroot permet d'installer facilement les paquets de nombreuses applications dans le système de fichiers de l'image. Malheureusement, aucun paquet n'existe pour _Clonezilla_. L'idée de créer un paquet pour ce logiciel a été étudiée, mais n'a pas été retenue, car il nécessite de -nombreuses dépendances, et bien que cela aurait été possible de le -faire, le temps à investir dans cette tâche aurait été considérable, et -le résultat n'aurait été testable qu'une fois cette tâche accomplie. De +nombreuses dépendances. Bien que cela aurait été possible de le faire, +le temps à investir dans cette tâche aurait été considérable, et le +résultat n'aurait été testable qu'une fois cette tâche accomplie. De plus, pour chaque mise à jour de _Clonezilla_ que l'on souhaiterait utiliser, par exemple pour profiter de l'implémentation d'un nouveau système de fichier ou des résolutions de bugs, il faudrait remettre à @@ -1338,7 +1336,7 @@ système d'exploitation a donc été modifiée pour tenir compte de ce changement de taille. Le système initial chargeait à chaque fois une image entière du système de fichiers racine (environ 100MB) en mémoire. Sur le nouveau système, seul le noyau linux est téléchargé, ainsi que -l'image _initrd_. Le sytème de fichier racine est monté à partir d'un +l'image _initrd_. Le sytème de fichiers racine est monté à partir d'un partage !!acronym{NFS}. Cela permet d'avoir un système de fichiers racine contenant beaucoup plus de données, mais de ne télécharger à travers le réseau que ceux qui sont utilisés, au moment où ils sont @@ -1347,11 +1345,11 @@ utilisés. ### Création du système de fichiers racine _Debian_ avec _multistrap_ Le système de fichiers racine accessible via un partage !!acronym{NFS} -est créé en utilisant l'outil _multistrap_ fourni par Debian. Cet outil -permet de générer un système de fichiers racine pour une architecture -donnée, et d'y installer une liste de paquets choisie. Une fois le -sytème de fichiers racine créé, certains fichiers de configurations -doivent sont créés manuellement: +est créé en utilisant l'outil _multistrap_ fourni par _Debian_. Cet +outil permet de générer un système de fichiers racine pour une +architecture donnée, et d'y installer une liste de paquets choisie +!!cite{multistrap_man}. Une fois le sytème de fichiers racine créé, +certains fichiers de configurations doivent sont créés manuellement: - `/etc/hostname`: le nom d'hôte de la machine. Dans notre cas, la valeur `bootiful-deployer` a été entrée. @@ -1457,8 +1455,8 @@ en plusieurs "étages" (_multi-stage build_) a été utilisée. Trois !!acronym{GRUB} pour lancer le système de déploiement. Le fichier `Makefile` à la racine du projet définit des cibles et des -recettes pour exécuter `docker build` exporter les données des deux -derniers étages dans leurs dossiers respectifs: +recettes pour exécuter `docker build` afin d'exporter les données des +deux derniers étages dans leurs dossiers respectifs: - le contenu de l'étage `nfs-export-stage`, c'est à dire l'archive `nfsroot.tar.gz`, est exportée dans le dossier `nfs/`. Elle sera @@ -1471,11 +1469,11 @@ derniers étages dans leurs dossiers respectifs: ## Déploiement d'image Windows Une image _Windows 10_ a été créée avec _Clonezilla_, à partir du -système présent par défaut sur les mini-pc DELL que l'école avait mis à -disposition. Les PC étant neufs il s'agit du système d'exploitation par -défaut installé pour le constructeur. L'image qui a été créée a déjà été -mentionnée dans la section _Réduction de la taille des images_. Elle a -la particularité d'être très grande car elle prend l'espace entier du +système présent par défaut sur les mini-pc _DELL_ que l'école avait mis +à disposition. Les PC étant neufs il s'agit du système d'exploitation +par défaut installé pour le constructeur. L'image qui a été créée a déjà +été mentionnée dans la section _Réduction de la taille des images_. Elle +a la particularité d'être très grande car elle prend l'espace entier du disque. Un problème a été rencontré lors du premier déploiement de l'image: le @@ -1493,10 +1491,11 @@ d'une image _Windows_ est faisable avec ce système. ## Réduction du temps de déploiement total -Théoriquement, le déploiement d'images avec clonezilla devrait être plus -court que le déploiement d'images _raw_. Des mesures ont été effectuées -avec les trois images dont les différences de taille ont déjà étudiées -précédemment dans la section _Réduction de la taille des images_: +Théoriquement, le déploiement d'images avec _Clonezilla_ devrait être +plus court que le déploiement d'images _raw_. Des mesures ont été +effectuées avec les trois images dont les différences de taille ont déjà +étudiées précédemment dans la section _Réduction de la taille des +images_: 1. Une petite image (environ 3.2 GB) du système _Debian_, dont les temps de déploiement sont comparés dans le tableau @@ -1691,10 +1690,9 @@ plusieurs problèmes avec ce système: difficile car il faut trouver où les déploiements commencent et se terminent dans le même fichier. -Le système de log a donc été modifié pour qu'un fichier par déploiement -soit créé par déploiement. Chaque fichier de log est nommé selon -l'adresse mac de la machine ainsi que la date et l'heure du début du -déploiement. +Le système de log a donc été modifié pour qu'un fichier distinct soit +créé par déploiement. Chaque fichier de log est nommé selon l'adresse +mac de la machine ainsi que la date et l'heure du début du déploiement. ### Intégration de _Clonezilla_ comme type d'image @@ -1742,7 +1740,7 @@ présents sur la partition !!acronym{ESP} qui vient d'être déployée. Cette conversion est importante car ce chemin devra être passé à !!acronym{GRUB}, qui ne sait lire que des chemins au format _Unix_. -Une fois le point d'entrée déterminé un fichier `efi_entrypoint` est +Une fois le point d'entrée déterminé, un fichier `efi_entrypoint` est écrit à la racine de la partition !!acronym{ESP}. Il sera ensuite lu par !!acronym{GRUB} au prochain démarrage pour trouver le point d'entrée à exécuter. Le contenu de ce fichier ressemblera à ceci: @@ -1781,13 +1779,13 @@ consiste uniquement à copier/remplacer un fichier de configuration à un endroit précis. Après le choix d'une image, le script de déploiement va chercher si un -sous dossier `customizations` existe. Ce dossier contient un +sous-dossier `customizations` existe. Ce dossier contient un sous-dossier par personnalisation que l'utilisateur peut choisir. Chaque -sous dossier personnalisation doit contenir une arborescence de dossiers -et de fichiers dont le premier niveau contient uniquement des dossier +sous-dossier personnalisation doit contenir une arborescence de dossiers +et de fichiers dont le premier niveau contient uniquement des dossiers correspondant à des noms de partitions existant dans l'image déployée. À -l'intérieur de ce dossier, une arborescence des fichiers copiés sur la -machine depuis laS +l'intérieur de ce dossier, une arborescence des fichiers qui doivent +être copiés sur la machine. Étudions une arborescence fictive de personnalisations pour mieux comprendre ce système. Voici l'arborescence du dossier `customizations` @@ -1816,12 +1814,12 @@ customizations Cette arborescence définit les personnalisations suivantes, qui peuvent être choisies par l'utilisateur: -1. `C language development environment`: copie un _playbook_ _Ansible_ +1. *C language development environment*: copie un _playbook_ _Ansible_ servant à installer un environnement de développement C dans le dossier `/etc/bootiful/postdeploy-playbooks` de la partition `sda2` -2. `Custom bashrc`: remplace le ficher `/etc/bash.bashrc` de la +2. *Custom bashrc*: remplace le ficher `/etc/bash.bashrc` de la partition `sda2` par un fichier personnalisé. -3. `Join LDAP domain`: copie un _playbook_ _Ansible_ servant à rejoindre +3. *Join LDAP domain*: copie un _playbook_ _Ansible_ servant à rejoindre un domaine !!acronym{LDAP} dans le dossier `/etc/bootiful/postdeploy-playbooks` de la partition `sda2` @@ -1831,7 +1829,7 @@ Les personnalisations **1** et **3** nécessitent que l'image soit dans `/etc/bootiful/postdeploy-playbooks` au démarrage. Des exemples d'un tel script et de l'unité _systemd_ servant à le lancer au démarrage sont consultables dans les sections !!ref{source_ansible_run} et -!!ref{source_ansible_init}.'w'''' +!!ref{source_ansible_init}. La personnalisation **2** est passive: elle remplace juste un fichier mais ne nécessite pas de mécanisme spécial sur la machine de déploiement @@ -1981,7 +1979,7 @@ gardant le système de déploiement indépendant de ce qui est utilisé pour les exécuter, car le seul mécanisme présent dans le système de déploiement est la copie de fichiers sur une partition donnée. -## Points d'amélioration +## Améliorations possibles Bien que le système à la fin de ce projet soit parfaitement fonctionnel, il existe des points qui devraient ou pourraient être étudiés, améliorés diff --git a/doc/rapport.md b/doc/rapport.md index cb634ccdb8b361dcf9e405d6385e776b3e653ae6..97ba90d445365405f07c908c83387e9500dc64fa 100644 --- a/doc/rapport.md +++ b/doc/rapport.md @@ -111,15 +111,17 @@ système d’exploitation (<abbr title="Operating System: système d’exploitat d’être administrateur sur la machine physique. En effet, utiliser une machine virtuelle n’est pas toujours souhaitable, surtout lorsqu’il y a interaction avec du matériel, typiquement lors du développement pour -l’embarqué. Le but de ce projet est de développer un système de -déploiement répondant à ces besoins. Lors de la mise sous tension d’une -machine, le système proposerait à l’utilisateur un choix d’OSes et -celui-ci s’installerait à la volée (distributions Linux, différentes -versions de Windows, etc.). Afin de minimiser le temps d’attente lors de +l’embarqué. + +Le but de ce projet est de développer un système de déploiement +répondant à ces besoins. Lors de la mise sous tension d’une machine, le +système proposerait à l’utilisateur un choix d’OSes et celui-ci +s’installerait à la volée (distributions Linux, différentes versions de +Windows, etc.). Afin de minimiser le temps d’attente lors de l’installation de l’<abbr title="Operating System: système d’exploitation ">OS</abbr>, un mécanisme de cache évitant le téléchargement des images préalablement téléchargées sera mis en place. On désire aussi que le système de déploiement permette de customiser -certains aspects de l’image choisie. Ce travail ce base sur une ébauche +certains aspects de l’image choisie. Ce travail se base sur une ébauche d’un travail déjà existant. ## Objectifs @@ -149,11 +151,11 @@ Les objectifs du projet, tirés de l´énoncé de ce dernier ## Déroulement -Ce projet n'a pas n'a pas été réalisé dans les locaux d'<abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> -mais à distance, depuis la maison, en partie à cause de la situation -sanitaire due au COVID-19 mais aussi parce qu'il est effectué dans le -cadre d'un bachelor en cours du soir, en parallèle à une activité -professionnelle à 80%. +Ce projet n'a pas été réalisé dans les locaux d'<abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> mais à +distance, depuis la maison, en partie à cause de la situation sanitaire +due au COVID-19 mais aussi parce qu'il est effectué dans le cadre d'un +bachelor en cours du soir, en parallèle à une activité professionnelle à +80%. Des rendez-vous hebdomadaires avec le professeur responsable Florent Glück ainsi que l'assistant Sébastien Chassot ont été organisés tous les @@ -161,10 +163,11 @@ mercredis, afin d'assurer un suivi du projet. ## Matériel à disposition -Deux mini-ordinateurs ont étés prêtés par <abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> pour la durée +Deux mini-ordinateurs ont été prêtés par <abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> pour la durée du projet, afin d'effectuer des tests sur du matériel réel. Les spécifications de ces ordinateurs sont définies dans la table -. +. Un photographie d'un ordinateur de ce type est +visible dans la figure . Spécifications techniques des ordinateurs mis à disposition: <a name="mini_pc_specs"/> @@ -174,11 +177,12 @@ Spécifications techniques des ordinateurs mis à disposition: <a name="mini_pc_ |:----------------|:--------------------------------------------------------------------| | Constructeur | _DELL_ | | Modèle | _Optiplex 7060 micro_ | -| Processeur | _Intel® Core™ i7-8700 3.20GHz_ | -| Mémoire vive | 8 GiB (2 barettes 4GiB _SODIMM DDR4 Synchronous_ 2666 MHz (0.4 ns)) | +| Processeur | _Intel Core i7-8700_ 3.2GHz | +| Mémoire vive | 8 GiB (2×4GiB _SODIMM DDR4 Synchronous_ 2666 MHz (0.4 ns)) | | Disque | _TOSHIBA KSG60ZMV_ SSD 476GiB (512GB) | | Réseau | _Intel e1000e Gigabit Ethernet_ | + ## Méthodologie @@ -217,8 +221,8 @@ mais aucun d'entre eux ne répond précisément aux objectifs précis du cahier des charges de ce projet. Les sous-sections qui suivent décrivent quelques-uns de ces systèmes, -leurs points forts et points faibles et pourquoi ils ne sont pas adaptés -tels-quel pour ce projet. +leurs points forts et points faibles ainsi que pourquoi ils ne sont pas +adaptés tels quels pour ce projet. ### BpBatch / Rembo / <abbr title="International Business Machines corporation ">IBM</abbr> Tivoli @@ -244,12 +248,12 @@ projet a été intégré à leur solution _Tivoli Provisionning Manager_ Ce système répond aux besoins de <abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr>, et il a même déjà été utilisé au sein de l'institution par le passé. Il a cependant été -abandonné à cause de sa licence devenue très couteuse et de sa -complexité devenue trop grande pour les besoins simple de l'école. +abandonné à cause de sa licence devenue très coûteuse et de sa +complexité devenue trop grande pour les besoins simples de l'école. Le développement d'une solution alternative _open-source_ plus simple et -se basant sur des briques logicielles existantes serait donc très -intéressante. +se basant sur des briques logicielles existantes est donc +particulièrement intéressante. ### <abbr title="Free Open-source Ghost ">FOG</abbr> project @@ -258,25 +262,25 @@ déployer des images de systèmes d'exploitation sur des postes clients. Elle fonctionne avec une architecture client-serveur. Le serveur est -accédé par une interface web, où un administrateur peut gérer ses images -et les déployer sur des clients qui démarrent un logiciel de déploiement -avec <abbr title="Pre-boot eXecution Environment: environnement d’exécution pré-démarrage ">PXE</abbr>. +accédé par une interface web, dans laquelle un administrateur peut gérer +ses images et les déployer sur des clients qui démarrent un logiciel de +déploiement avec <abbr title="Pre-boot eXecution Environment: environnement d’exécution pré-démarrage ">PXE</abbr>. -Cette solution est utilisées dans certaines classes à <abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> -pour déployer des images sur certains postes, notemment pour les travaux +Cette solution est utilisée dans certaines classes à <abbr title="Haute École du Paysage, d’Ingénierie et d’Architecture de Genève ">HEPIA</abbr> +pour déployer des images sur certains postes, notamment pour les travaux de laboratoire de réseau. Malheureusement, ce système ne répond pas exactement au cahier des charges de ce projet: il est plutôt conçu pour "pousser" des images sur des clients, depuis le serveur, après une initiation du déploiement par un administrateur. Nous souhaitons plutôt un système ne nécessitant pas -d'intervention d'un administrateur pour initier un déploiement. +d'intervention d'un administrateur. -De plus,<abbr title="Free Open-source Ghost ">FOG</abbr> ne dispose pas de mécanisme de cache sur les +De plus, <abbr title="Free Open-source Ghost ">FOG</abbr> ne dispose pas de mécanisme de cache sur les clients permettant de ne pas télécharger les images plusieurs fois. À -chaque fois qu'un image doit être déployée, elle doit être téléchargée à -nouveau depuis le serveur, ce qui peut prendre beaucoup de temps et doit -être planifié à l'avance. +chaque fois qu'une image doit être déployée, elle doit être téléchargée +à nouveau depuis le serveur, ce qui peut prendre beaucoup de temps et +doit être planifié à l'avance. ### Clonezilla @@ -289,12 +293,13 @@ Cet outil peut être utilisé de deux manières: système d'exploitation de déploiement depuis un un serveur <abbr title="Pre-boot eXecution Environment: environnement d’exécution pré-démarrage ">PXE</abbr>. Ils peuvent ensuite initier le déploiement ou la restauration d'une image depuis/vers ce serveur. -- En faisant démarrer les clients sur un CD ou une clé USB. Ils peuvent - ensuite initier le déploiement ou la restauration d'une image - d'<abbr title="Operating System: système d’exploitation ">OS</abbr> depuis/vers sur un autre disque ou un serveur distant. +- En faisant démarrer les clients sur un CD ou une clé USB. Ils est + ensuite possible d'initier le déploiement ou la restauration d'une + image d'<abbr title="Operating System: système d’exploitation ">OS</abbr> depuis/vers sur un autre disque ou un serveur + distant. Un avantage de _Clonezilla_ est le format utilisé pour la sauvegarde des -images. L'outil _Partclone_, développé par la même équipe, est utilisé. +images. L'outil utilisé, _Partclone_ est développé par la même équipe. Il permet de créer des images de partitions en ne copiant que les blocs utilisés dans le système de fichiers. Les parties non utilisées de la partition ne sont pas comprises dans l'image créée, ce qui permet de @@ -305,10 +310,10 @@ Malheureusement, comme pour <abbr title="Free Open-source Ghost ">FOG</abbr>, _C de mécanisme de cache sur les clients permettant de ne pas télécharger les images depuis un serveur distant plusieurs fois. À chaque fois qu'un image doit être déployée, elle doit être téléchargée à nouveau depuis le -serveur, ce qui peut prendre beaucoup de temps et doit être planifié à -l'avance. +serveur. Cette action peut prendre beaucoup de temps et doit être +planifiée à l'avance. -_Clonezilla_ ne peut donc pas être utilisé tel-quel. Cependant, il est +_Clonezilla_ ne peut donc pas être utilisé tel quel. Cependant, il est possible de l'utiliser uniquement pour la création d'images et d'intégrer son mécanisme de restauration à un autre système de déploiement. Cette possibilité sera étudiée plus tard dans ce document. @@ -359,20 +364,20 @@ addressable du disque, se trouve le secteur d'amorçage au format <abbr title="Master Boot Record: enregistrement d’amorcage maître ">MBR</abbr>. Ce premier secteur contient deux informations nécessaires pour le chargement d'un système d'exploitation: -1. **La routine d'amorcage**: c'est le premier programme exécuté lors du - démarrage sur le disque. Sa taille est très limitée: 440 octets. Il - sert généralement à initier un chargeur d'amorcage ou _bootloader_ - présent un peu plus loin sur le disque, qui est un petit programme - qui va initier un système d'exploitation. +1. **La routine d'amorcage**: il s'agit du premier programme exécuté + lors du démarrage sur le disque. Sa taille est très limitée: 440 + octets. Il sert généralement à initier un chargeur d'amorcage ou + _bootloader_ présent un peu plus loin sur le disque, qui est un petit + programme qui va initier un système d'exploitation. 2. **La table des partitions**: définit une liste de partitions, qui sont des subdivisions logiques de l'espace du disque. Selon le système d'exploitation installé et la manière dont il a été configuré - lors de son installation initiale les partitions peuvent varier en + lors de son installation initiale, les partitions peuvent varier en nombre et en taille. Les données à l'intérieur de chaque partition sont généralement structurées en utilisant un système de fichiers. - Les partitions vont varier en nombre, taille, et format selon l'image - déployée. + Les partitions vont différer en nombre, taille, et format selon + l'image déployée. La seconde partie du disque est l'espace dédié à la mise en cache d'images d'<abbr title="Operating System: système d’exploitation ">OS</abbr>, qui prend 20% de l'espace total disponible, @@ -400,7 +405,7 @@ Enfin, les 4096 octets restants à la fin du disque sont dédiés à l'écriture d'une signature servant à signaler si une image vient d'être deployée sur le disque. La présence cette signature est verifiée à chaque démarrage pour déterminer s'il faut démarrer une image -fraichement déployée ou si au contraire l'image déployée a déjà été +fraîchement déployée ou si au contraire l'image déployée a déjà été utilisée et qu'il faut lancer un nouveau déploiement. Ainsi, le système peut s'assurer qu'une image disque déployée n'est exécutée qu'une seule fois, ce qui laisse la liberté à l'utilisateur du poste client de faire @@ -409,7 +414,7 @@ système inutilisable, car il suffit de redémarrer la machine pour repartir sur une base propre. Le mécanisme de signature est nécessaire à cause du moyen choisi pour -déployer les images: un système d'exploitation Linux minimaliste est est +déployer les images: un système d'exploitation Linux minimaliste est chargé en mémoire depuis le réseau afin de lancer le processus de choix d'une image et de la déployer. Une fois le déploiement terminé, la machine redémarre. La signature à la fin du disque est un moyen simple @@ -418,7 +423,7 @@ déployée et de la vérifier au démarrage. ### Serveur -Le composant central du système est un *serveur* linux qui inter-agit +Le composant central du système est un *serveur* linux qui interagit avec les postes clients pour leur permettre d'effectuer plusieurs actions: @@ -458,7 +463,7 @@ d'effectuer ces actions: du fichier exécutable du chargeur d'amorçage à récupérer sur ce dernier -- <abbr title="Trivial File Transfer Protocol: protocole simplifié de transfert de fichiers ">TFTP</abbr>: permet au clients de télécharger: +- <abbr title="Trivial File Transfer Protocol: protocole simplifié de transfert de fichiers ">TFTP</abbr>: permet aux clients de télécharger: - le fichier exécutable du chargeur d'amorçage - la configuration du chargeur d'amorçage - les modules optionels du chargeur d'amorçage, téléchargés selon la @@ -490,9 +495,6 @@ différents composants et leurs interactions. ## Processus de déploiement initial -**TODO: décrire pas à pas les différentes étapes du processus de -déploiement telles que montrées sur le diagramme.** - Les différentes étapes du processus de déploiement d'images sont décrites dans la figure qui est un diagrame d'activité détaillant les différentes étapes du processus de déploiement @@ -532,13 +534,14 @@ connecter à deux réseaux locaux distincts. Le serveur étant une machine virtuelle _VirtualBox_ créée par l'outil _Vagrant_, ses interfaces réseau sont aussi virtuelles. Ces interfaces -virtuelles ont été ratachées aux interfaces de la machine hôte de la +virtuelles ont été rattachées aux interfaces de la machine hôte de la manière suivante: -1. L'interface `eth0` de la machine virtuelle est ratachée à l'interface - `wlan0` de la machine hôte en utilisant le mode <abbr title="Network Address Translation: traduction d’adresse réseau ">NAT</abbr>. -2. L'interface `eth1` de la machine virtuelle est ratachée à l'interface - `eth0` de la machine hôte en utilisant le mode `bridged`. +1. L'interface `eth0` de la machine virtuelle est rattachée à + l'interface `wlan0` de la machine hôte en utilisant le mode + <abbr title="Network Address Translation: traduction d’adresse réseau ">NAT</abbr>. +2. L'interface `eth1` de la machine virtuelle est rattachée à + l'interface `eth0` de la machine hôte en utilisant le mode `bridged`. La figure illustre les détails de la configuration réseau décrite ci-dessus. @@ -594,9 +597,9 @@ d'intervention possible. Le programme _Vagrant_ a été utilisé dans un premier temps pour automatiser la création d'une machine virtuelle à partir d'un fichier `Vagrantfile` définissant la configuration de la machine et les commandes à lancer pour l'installer et la configurer. Une -fois ce fichier de configuration créé, une seule commande est nécessaire -pour créer, configurer et lancer un serveur fonctionnel dans une machine -virtuelle: `vagrant up`. +fois ce fichier de configuration produit, une seule commande est +nécessaire pour créer, configurer et lancer un serveur fonctionnel dans +une machine virtuelle: `vagrant up`.  @@ -619,8 +622,8 @@ il comporte de nombreuses limitations auxquelles il faudrait palier: commande, ce n'est pas très facile à utiliser. - Les scripts de déploiement sont fragiles et gèrent mal les erreurs. - Il n'y a pas de système de personnalisation d'image. Si on veut faire - $`n`$ personnalisation partant sur la base du même - <abbr title="Operating System: système d’exploitation ">OS</abbr>, il faut faire $`n`$ images: une pour chaque + $`n`$ personnalisation(s) partant sur la base du même + <abbr title="Operating System: système d’exploitation ">OS</abbr>, il faut faire $`n`$ image(s): une pour chaque personnalisation. - L'installation de nouveaux outils sur le système d'exploitation de l'<abbr title="Operating System: système d’exploitation ">OS</abbr> de déploiement créé avec _Buildroot_ peut être @@ -695,8 +698,8 @@ cette approche est qu'il est difficile de mettre à jour <abbr title="GRand Unif sur une nouvelle version. Une nouvelle approche a été choisie pour intégrer le module `isign` à -<abbr title="GRand Unified Bootloader ">GRUB</abbr>, qui permet de mettre à jour plus facilement -<abbr title="GRand Unified Bootloader ">GRUB</abbr>, qui a d'ailleurs été utilisé en version 2.04, la +<abbr title="GRand Unified Bootloader ">GRUB</abbr>, qui permet de mettre à jour plus facilement ce +programme, qui a d'ailleurs été mis à jour à la version 2.04 qui est la dernière version sortie au moment où cela a été fait. La nouvelle approche est la suivante: @@ -712,13 +715,6 @@ L'avantage de cette nouvelle approche est qu'il est maintenant facile d'intégrer les changements survenus dans le dépôt git officiel sur son clone pour bénéficier des dernières mises à jour de <abbr title="GRand Unified Bootloader ">GRUB</abbr>. - - -**TODO: expliquer les problèmes rencontrés sans <abbr title="Extensible Firmware Interface: interface micrologicielle extensible unifiée ">EFI</abbr> avec -lesmachines récentes, des modifications nécessaires pour supporter ce -nouveau système et des limitations que cela apporte.** - - ## Amélioration de la vitesse de transfert des images Le protocole <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> est utilisé dans le système initial pour @@ -755,13 +751,13 @@ Les différents protocoles qui ont été mesurés sont: plus rapide, car permet de faire plus qu'un simple transfert de fichiers. - <abbr title="Server Message Block ">SMB</abbr>: le protocole standard pour monter à distance des - dossier partagés entre des <abbr title="Operating System: système d’exploitation ">OS</abbr> Windows. Une implémentation - existe sous linux, _Samba_, et elle est courremment utilisée pour une - meilleure compatiblité avec les systèmes Windows que sont équivalent + dossiers partagés entre des <abbr title="Operating System: système d’exploitation ">OS</abbr> Windows. Une implémentation + existe sous linux, _Samba_, et elle est couramment utilisée pour une + meilleure compatibilité avec les systèmes Windows que son équivalent <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>. - <abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr>: ce protocole pair à pair décentralisé permet à chacun - de des pairs de mettre à disposition ou de télécharger des fichiers. - Il pourrait être intéressant d'utiliser ce protocole pour soulager la + des pairs de mettre à disposition ou de télécharger des fichiers. Il + pourrait être intéressant d'utiliser ce protocole pour soulager la charge du serveur, car tous les clients connectés peuvent se partager des parties d'images qu'ils ont déjà. Le serveur de déploiement ne serait qu'un pair de plus, qui dispose de toutes les images. Il est @@ -1151,7 +1147,7 @@ est: `QmX9MYUQhjKxua6HQMtpzaZd9ui4gGT75FJgAxeQJC47Ei` -On peutmaintenant le rajouter dans la configuration des noeuds +On peut maintenant le rajouter dans la configuration des noeuds d'amorçage des deux machines en lançant la commande suivante sur chacune d'entre elle: @@ -1159,7 +1155,7 @@ d'entre elle: IPFS_PATH=~/.ipfs ipfs bootstrap add /dnsaddr/debian1.home/p2p/QmX9MYUQhjKxua6HQMtpzaZd9ui4gGT75FJgAxeQJC47Ei ``` -Maintenant, le _daemon_ <abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr> peut être lancé sur chacune des +À présent, le _daemon_ <abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr> peut être lancé sur chacune des machines. La variable d'environnement `LIBP2P_FORCE_PNET=1` est définie pour forcer les échanges à se faire sur un réseau privé. @@ -1173,8 +1169,8 @@ terminal sera ouvert pour la suite des commandes. Dans une configuration plus durable, il faudrait le faire tourner en arrière plan, par exemple avec une unité systemd. -Maintenant on peut partager l'image sur le réseau avec la commande -suivante sur `debian1`: +On peut enfin partager l'image sur le réseau avec la commande suivante +sur `debian1`: ```bash $ IPFS_PATH=~/.ipfs ipfs add win10.tar.gz @@ -1186,7 +1182,7 @@ Le temps d'ajout de l'image a pris plus de trois minutes. À la fin, la référence du fichier, qui l'identifie de manière unique sur le réseau, est affichée: `QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv`. -On peut maintenant tenter de récupérer le fichier depuis `debian2`: +On peut alors tenter de récupérer le fichier depuis `debian2`: ```bash $ IPFS_PATH=~/.ipfs ipfs get QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv @@ -1194,9 +1190,9 @@ Saving file(s) to QmRm8As8ECuQoLq3UWowsxJ8mh89txLvpUh7A2mgw5pMhv 12.80 GiB / 12.80 GiB [==============================================================================] 100.00% 7m27s ``` -Le transfert a pris $`7m27s = 447s`$. C'est beaucoup trop long -pour nos besoins. Le benchmark avec `perf` n'a même pas été effectué au -vu de ce résultat. +Le transfert a pris $`7m27s = 447s`$. Ce résultat est beaucoup +trop long pour nos besoins. Le benchmark avec `perf` n'a même pas été +effectué au vu de ce résultat. ### Choix du protocole de transfert de fichiers @@ -1204,7 +1200,7 @@ La table ainsi que la figure récapitulent les résultats mesurés pour chacun des protocoles testés. On peut remarquer que pour tous les protocoles, sauf <abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr>, les résultats sont proches de 120 secondes. Cette -durée montre que pour ces protocoles, on est proches de la limite +durée montre que pour ces protocoles, on se rapproche de la limite théorique de gigabit ethernet: @@ -1214,7 +1210,7 @@ théorique de gigabit ethernet: On peut donc conclure que le choix du protocole <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> est -approprié car les autres protocole n'accélèrent pas considérablement la +approprié car les autres protocoles n'accélèrent pas considérablement la vitesse de transfert d'une image. @@ -1269,7 +1265,7 @@ l'image et seuls les blocs utilisés sont ensuite restaurés. Les images contentant uniquement les blocs utilisés sont ensuite compressées avec `gzip`. -La taille des images créées avec clonezilla a été comparée à la taille +La taille des images créées avec _Clonezilla_ a été comparée à la taille des images déployées et à la taille des images juste compressées avec `gzip`, avec trois images: @@ -1374,15 +1370,15 @@ générer des systèmes embarqués. Il avait été choisi dans le système initial car il permettait de créer une image d'<abbr title="Operating System: système d’exploitation ">OS</abbr> très petite qui peut être téléchargée très rapidement sur le réseau. -### Problèmes avec_Buildroot_ pour construire l'<abbr title="Operating System: système d’exploitation ">OS</abbr> de déploiement +### Problèmes avec _Buildroot_ pour construire l'<abbr title="Operating System: système d’exploitation ">OS</abbr> de déploiement Buildroot permet d'installer facilement les paquets de nombreuses applications dans le système de fichiers de l'image. Malheureusement, aucun paquet n'existe pour _Clonezilla_. L'idée de créer un paquet pour ce logiciel a été étudiée, mais n'a pas été retenue, car il nécessite de -nombreuses dépendances, et bien que cela aurait été possible de le -faire, le temps à investir dans cette tâche aurait été considérable, et -le résultat n'aurait été testable qu'une fois cette tâche accomplie. De +nombreuses dépendances. Bien que cela aurait été possible de le faire, +le temps à investir dans cette tâche aurait été considérable, et le +résultat n'aurait été testable qu'une fois cette tâche accomplie. De plus, pour chaque mise à jour de _Clonezilla_ que l'on souhaiterait utiliser, par exemple pour profiter de l'implémentation d'un nouveau système de fichier ou des résolutions de bugs, il faudrait remettre à @@ -1409,7 +1405,7 @@ système d'exploitation a donc été modifiée pour tenir compte de ce changement de taille. Le système initial chargeait à chaque fois une image entière du système de fichiers racine (environ 100MB) en mémoire. Sur le nouveau système, seul le noyau linux est téléchargé, ainsi que -l'image _initrd_. Le sytème de fichier racine est monté à partir d'un +l'image _initrd_. Le sytème de fichiers racine est monté à partir d'un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>. Cela permet d'avoir un système de fichiers racine contenant beaucoup plus de données, mais de ne télécharger à travers le réseau que ceux qui sont utilisés, au moment où ils sont @@ -1418,11 +1414,11 @@ utilisés. ### Création du système de fichiers racine _Debian_ avec _multistrap_ Le système de fichiers racine accessible via un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> -est créé en utilisant l'outil _multistrap_ fourni par Debian. Cet outil -permet de générer un système de fichiers racine pour une architecture -donnée, et d'y installer une liste de paquets choisie. Une fois le -sytème de fichiers racine créé, certains fichiers de configurations -doivent sont créés manuellement: +est créé en utilisant l'outil _multistrap_ fourni par _Debian_. Cet +outil permet de générer un système de fichiers racine pour une +architecture donnée, et d'y installer une liste de paquets choisie +. Une fois le sytème de fichiers racine créé, +certains fichiers de configurations doivent sont créés manuellement: - `/etc/hostname`: le nom d'hôte de la machine. Dans notre cas, la valeur `bootiful-deployer` a été entrée. @@ -1528,8 +1524,8 @@ en plusieurs "étages" (_multi-stage build_) a été utilisée. Trois <abbr title="GRand Unified Bootloader ">GRUB</abbr> pour lancer le système de déploiement. Le fichier `Makefile` à la racine du projet définit des cibles et des -recettes pour exécuter `docker build` exporter les données des deux -derniers étages dans leurs dossiers respectifs: +recettes pour exécuter `docker build` afin d'exporter les données des +deux derniers étages dans leurs dossiers respectifs: - le contenu de l'étage `nfs-export-stage`, c'est à dire l'archive `nfsroot.tar.gz`, est exportée dans le dossier `nfs/`. Elle sera @@ -1542,11 +1538,11 @@ derniers étages dans leurs dossiers respectifs: ## Déploiement d'image Windows Une image _Windows 10_ a été créée avec _Clonezilla_, à partir du -système présent par défaut sur les mini-pc DELL que l'école avait mis à -disposition. Les PC étant neufs il s'agit du système d'exploitation par -défaut installé pour le constructeur. L'image qui a été créée a déjà été -mentionnée dans la section _Réduction de la taille des images_. Elle a -la particularité d'être très grande car elle prend l'espace entier du +système présent par défaut sur les mini-pc _DELL_ que l'école avait mis +à disposition. Les PC étant neufs il s'agit du système d'exploitation +par défaut installé pour le constructeur. L'image qui a été créée a déjà +été mentionnée dans la section _Réduction de la taille des images_. Elle +a la particularité d'être très grande car elle prend l'espace entier du disque. Un problème a été rencontré lors du premier déploiement de l'image: le @@ -1564,10 +1560,11 @@ d'une image _Windows_ est faisable avec ce système. ## Réduction du temps de déploiement total -Théoriquement, le déploiement d'images avec clonezilla devrait être plus -court que le déploiement d'images _raw_. Des mesures ont été effectuées -avec les trois images dont les différences de taille ont déjà étudiées -précédemment dans la section _Réduction de la taille des images_: +Théoriquement, le déploiement d'images avec _Clonezilla_ devrait être +plus court que le déploiement d'images _raw_. Des mesures ont été +effectuées avec les trois images dont les différences de taille ont déjà +étudiées précédemment dans la section _Réduction de la taille des +images_: 1. Une petite image (environ 3.2 GB) du système _Debian_, dont les temps de déploiement sont comparés dans le tableau @@ -1801,10 +1798,9 @@ plusieurs problèmes avec ce système: difficile car il faut trouver où les déploiements commencent et se terminent dans le même fichier. -Le système de log a donc été modifié pour qu'un fichier par déploiement -soit créé par déploiement. Chaque fichier de log est nommé selon -l'adresse mac de la machine ainsi que la date et l'heure du début du -déploiement. +Le système de log a donc été modifié pour qu'un fichier distinct soit +créé par déploiement. Chaque fichier de log est nommé selon l'adresse +mac de la machine ainsi que la date et l'heure du début du déploiement. ### Intégration de _Clonezilla_ comme type d'image @@ -1852,7 +1848,7 @@ présents sur la partition <abbr title="EFI System Partition: partition système Cette conversion est importante car ce chemin devra être passé à <abbr title="GRand Unified Bootloader ">GRUB</abbr>, qui ne sait lire que des chemins au format _Unix_. -Une fois le point d'entrée déterminé un fichier `efi_entrypoint` est +Une fois le point d'entrée déterminé, un fichier `efi_entrypoint` est écrit à la racine de la partition <abbr title="EFI System Partition: partition système EFI ">ESP</abbr>. Il sera ensuite lu par <abbr title="GRand Unified Bootloader ">GRUB</abbr> au prochain démarrage pour trouver le point d'entrée à exécuter. Le contenu de ce fichier ressemblera à ceci: @@ -1891,13 +1887,13 @@ consiste uniquement à copier/remplacer un fichier de configuration à un endroit précis. Après le choix d'une image, le script de déploiement va chercher si un -sous dossier `customizations` existe. Ce dossier contient un +sous-dossier `customizations` existe. Ce dossier contient un sous-dossier par personnalisation que l'utilisateur peut choisir. Chaque -sous dossier personnalisation doit contenir une arborescence de dossiers -et de fichiers dont le premier niveau contient uniquement des dossier +sous-dossier personnalisation doit contenir une arborescence de dossiers +et de fichiers dont le premier niveau contient uniquement des dossiers correspondant à des noms de partitions existant dans l'image déployée. À -l'intérieur de ce dossier, une arborescence des fichiers copiés sur la -machine depuis laS +l'intérieur de ce dossier, une arborescence des fichiers qui doivent +être copiés sur la machine. Étudions une arborescence fictive de personnalisations pour mieux comprendre ce système. Voici l'arborescence du dossier `customizations` @@ -1926,12 +1922,12 @@ customizations Cette arborescence définit les personnalisations suivantes, qui peuvent être choisies par l'utilisateur: -1. `C language development environment`: copie un _playbook_ _Ansible_ +1. *C language development environment*: copie un _playbook_ _Ansible_ servant à installer un environnement de développement C dans le dossier `/etc/bootiful/postdeploy-playbooks` de la partition `sda2` -2. `Custom bashrc`: remplace le ficher `/etc/bash.bashrc` de la +2. *Custom bashrc*: remplace le ficher `/etc/bash.bashrc` de la partition `sda2` par un fichier personnalisé. -3. `Join LDAP domain`: copie un _playbook_ _Ansible_ servant à rejoindre +3. *Join LDAP domain*: copie un _playbook_ _Ansible_ servant à rejoindre un domaine <abbr title="Lightweight Directory Access Protocol: protocole léger d’accès à un annuaire ">LDAP</abbr> dans le dossier `/etc/bootiful/postdeploy-playbooks` de la partition `sda2` @@ -1943,2194 +1939,3 @@ d'un tel script et de l'unité _systemd_ servant à le lancer au démarrage sont consultables dans les sections et .'w'''' -La personnalisation **2** est passive: elle remplace juste un fichier -mais ne nécessite pas de mécanisme spécial sur la machine de déploiement -pour l'exécuter. - - - -# Conclusion - -## Synthèse du travail effectué - -Le travail initial a été récupéré, analysé, documenté et remis en -fonctionnement en corrigeant quelques problèmes dans les fichiers de -configuration et les scripts de déploiement. L'outil _Vagrant_ a été -utilisé pour automatiser la création d'un serveur virtuel qui exécute -les différents services du système. - -Le système a été ensuite modifié pour le rendre compatible avec les -ordinateurs modernes utilisant le standard <abbr title="Unified Extensible Firmware Interface: interface micrologicielle extensible unifiée ">UEFI</abbr> au lieu d'un -<abbr title="Basic Input Output System: système de base d’entrée sortie ">BIOS</abbr>. - -Des mesures ont été effectuées pour déterminer si le choix du protocole -<abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>, le protocole réseau utilisé pour transférer les images, -était pertinent. Il été comparé aux protocoles <abbr title="Secure CoPy: protocole de copie sécurisée sur le réseau ">SCP</abbr>, -<abbr title="HyperText Transfer Protocol: protocole de transfert hypertexte ">HTTP</abbr>, <abbr title="File Transfer Protocol: protocole de transfert de fichier ">FTP</abbr>, <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>, <abbr title="Server Message Block ">SMB</abbr> et -<abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr> en mesurant le temps de transfert d'une image. Ces -mesures ont montré que les temps de déploiement avec ces différents -protocoles étaient similaires, à l'exception d'<abbr title="InterPlanetary File System: système de fichier inter-planétaire ">IPFS</abbr> qui -prenait beaucoup plus de temps. Le protocole <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> a donc été -gardé car les mesures ont prouvé que ses performances étaient similaires -à ses alternatives. - -Un format d'images alternatif aux images brutes compressées a été -recherché. Une solution produisant des images plus petites et -déployables plus rapidement a été trouvée: utiliser des images produites -avec l'outil _Clonezilla_. Cet outil permet aussi de faciliter la -création d'images en utilisant sa version _live_. Des mesures prouvant -que ce système de déploiement d'images offre un réel avantage en espace -utilisé et en temps de déploiement par rapport aux images brutes -compressées. - -Le système d'exploitation utilisé pour effectuer le déploiement a été -complètement remplacé. Initialement construit avec _Buildroot_, avec un -système de fichiers racine chargé entièrement en mémoire, il a été -remplacé par un système _Debian_ dont le système de fichiers racine est -monté depuis un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> en lecture seule. Ce système -permet une installation aisée de tous les paquets _Debian_, notamment -_Clonezilla_ qui était compliqué à installer sur un système construit -avec _Buildroot_. La création de ce système d'exploitation a été -automatisée et encapsulée dans un conteneur _Docker_ pour rendre sa -modification et reconstruction très simple, indépendamment du système -d'exploitation utilisé. - -Les scripts de déploiement du système initial ont été réécrits. Une -gestion avancée des erreurs a été implémentée. Un système de mesure et -d'affichage du temps d'exécution de chaque partie du déploiement a été -développé. La génération de fichiers de log a été améliorée pour qu'ils -fournissent plus d'informations utiles et qu'ils soient structurés de -manière à rendre leur gestion et leur consultation plus aisée. Le -déploiement d'images avec _Clonezilla_ a été implémenté, tout en gardant -la possibilité d'utiliser des images brutes compressées pour offrir plus -de flexibilité et permettre de comparer les deux systèmes. Le support -des images de systèmes d'exploitations compatibles <abbr title="Unified Extensible Firmware Interface: interface micrologicielle extensible unifiée ">UEFI</abbr> a -aussi été implémenté, notamment en permettant de déterminer -automatiquement l'exécutable <abbr title="Extensible Firmware Interface: interface micrologicielle extensible unifiée ">EFI</abbr> qui devra être lancé pour -démarrer le système déployé. - -Enfin, un système de personnalisation d'un système après son déploiement -a été conçu, permettant de définir des configurations que l'utilisateur -peut choisir de copier ou non sur le système déployé. Une image de -système d'exploitation peut être conçue pour exécuter les fichiers -copiés par ces personnalisations dans un dossier spécifique, pour -permettre des configurations complexes exécutées lors du démarrage avec -un outil choisi, par exemple _Ansible_. Ce système offre beaucoup de -flexibilité et permet de définir des configurations avancées tout en -gardant le système de déploiement indépendant de ce qui est utilisé pour -les exécuter, car le seul mécanisme présent dans le système de -déploiement est la copie de fichiers sur une partition donnée. - -## Points d'amélioration - -Bien que le système à la fin de ce projet soit parfaitement fonctionnel, -il existe des points qui devraient ou pourraient être étudiés, améliorés -ou implémentés pour que le système soit prêt à une utilisation dans un -cadre réel. - -### Amélioration des performances de téléchargement des images sur un réseau réel avec de nombreux clients simultanés - -Le protocole réseau utilisé pour le téléchargement des images a été -testé avec un client unique, sur un réseau minimal ne comprenant que le -client et le serveur. Il est très probable que le temps de -téléchargement des images soit beaucoup plus long que celui mesuré dans -le cadre de ce travail si de nombreux clients téléchargent des images en -même temps, dans un réseau plus complexe tel que celui de l'école. - -L'étude et la mitigation de cette problématique a volontairement été -mise de côté dans le cadre de ce travail car il était difficile de -simuler les conditions réelles du système déployé dans une école alors -que tout le travail a été effectué hors de l'école, dans un petit réseau -local domestique avec une quantité limitée d'ordinateurs à disposition -pour faire des tests. - -Différentes approches pourraient être utilisées pour améliorer ces -performances, telles que la copie en _multicast_ des images, la -redondance des serveurs mettant à disposition les images ou encore -l'utilisation d'un protocole _pair à pair_ tel que _Bittorrent_ ou -_IPFS_ pour permettre de distribuer le transfert des images sur tous les -clients connectés plutôt que d'utiliser uniquement le serveur central -comme source du fichiers. - -### Amélioration du système de personnalisation des images - -Le système de personnalisation des images a été implémenté tardivement -dans le travail. Bien qu'il soit fonctionnel, il n'a pas été testé -extensivement. Il faudrait passer un peu de temps à créer des -configurations post-déploiement complexes qui sont exécutées au -démarrage par le système déployé et de valider leur fonctionnement. - -Par exemple, il était prévu de tester l'exécution de _playbooks_ -_Ansible_ au démarrage du système, copiés selon les choix de -personnalisation de l'utilisateur. Ces _playbooks_ pourraient étre -utilisés pour effectuer de nombreuses actions, telles que l'installation -de groupes de paquets choisis, la génération d'un nom d'hôte unique pour -la machine et la connexion à un domaine _Active Directory_ pour -permettre aux élèves de se connecter avec le même nom d'utilisateur et -d'avoir accès à leur dossier _home_ depuis un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>, -comme sur les postes de travail standards présents de l'école. - -### Amélioration de la sécurité - -Sur le système actuel, un utilisateur mal intentionné peut détruire ou -modifier les images présentes sur le serveur distant, car elles sont -récupérées sur un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> avec des droits de -lecture-écriture. La raison de ce partage en lecture et écriture est que -les logs sont écrits sur le même partage. Une manière simple de résoudre -ce problème serait de séparer ce partage en deux: le premier en lecture -seule contiendrait les images et le second contiendrait les logs -uniquement. - -Il faudrait ensuite trouver une solution pour empêcher une machine -d'aller modifier ou supprimer les logs d'une autre machine, ce qui est -compliqué à mettre en oeuvre avec un partage <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr>. Peut être -que les logs pourraient être transmis au serveur en utilisant un autre -canal de communication et de déléguer la création et le nommage du -fichier au serveur, pour que les clients ne soient plus capables -d'accéder directement aux fichiers des logs, mais que ce soit le serveur -qui ait la responsabilité de les créer et de les gérer. - -## Rétrospection sur le déroulement du travail - -J'ai eu beaucoup d'intérêt à travailler sur ce sujet. Cela m'a permis de -mettre en pratique de nombreuses compétences acquises lors des cours -suivis les quatre années précédentes, dont en particulier les cours de -programmation des systèmes (_sIT_242_), systèmes d'exploitation -(_sIT_244_), réseaux et protocoles informatiques (sIT_362), réseaux -avancés (_sIT_384_), programmation avancée des systèmes (_sIT_632_) et -virtualisation des <abbr title="Système d’Information ">SI</abbr> (_sIT_632_). - - - -# Codes sources notables - -Cette annexe contient les listings des codes sources les plus importants -du projet. - - - -## `Makefile`: configuration _GNU Make_ du projet - -```makefile -SHELL := /bin/bash -MAKEFLAGS += "-j 4" - -DOCKER_BUILDKIT_BUILD = DOCKER_BUILDKIT=1 docker build --progress=plain - -GRUB_SRC := $(shell find grub/bootiful-grub/ -type f -regex ".*\.[c|h|sh|py|cfg|conf]") -GRUB_I386_PC_BIN = tftp/tftpboot/boot/grub/i386-pc/core.0 -GRUB_I386_EFI_BIN = tftp/tftpboot/boot/grub/i386-efi/core.efi -GRUB_X86_64_EFI_BIN = tftp/tftpboot/boot/grub/x86_64-efi/core.efi -DEPLOYER_SRC := $(wildcard deployer/*) -TFTP_DEPLOYER_DIR = tftp/tftpboot/boot/deployer -TFTP_DEPLOYER_VMLINUZ := $(TFTP_DEPLOYER_DIR)/vmlinuz -TFTP_DEPLOYER_INITRD := $(TFTP_DEPLOYER_DIR)/initrd.img -NFS_DEPLOYER_ROOT := nfs/nfsroot.tar.gz -LATEST_LOG := $(shell ls -1 nfs/nfsshared/log/*.log | tail -n 1) - -.PHONY: doc grub deployer start-server reprovision-server clean print_last_log help - -# Builds everything -all: doc grub deployer - -# Builds PDF and markdown documents -doc: - $(MAKE) -C doc - -grub/bootiful-grub/bootstrap partclone/bootiful-partclone/Makefile.am: .gitmodules - git submodule init && git submodule update - -# Bootstraps GRUB dependencies and configuration script -grub/bootiful-grub/configure: grub/bootiful-grub/bootstrap - (pushd grub/bootiful-grub && ./bootstrap; popd) - -# Builds GRUB for i386-pc -$(GRUB_I386_PC_BIN): grub/Dockerfile grub/bootiful-grub/configure $(GRUB_SRC) - $(DOCKER_BUILDKIT_BUILD) ./grub \ - --output ./tftp/tftpboot \ - --build-arg PLATFORM=pc \ - --build-arg TARGET=i386 - -# Builds GRUB for i386-efi -$(GRUB_I386_EFI_BIN): grub/Dockerfile grub/bootiful-grub/configure $(GRUB_SRC) - $(DOCKER_BUILDKIT_BUILD) ./grub \ - --output ./tftp/tftpboot \ - --build-arg PLATFORM=efi \ - --build-arg TARGET=i386 - -# Builds GRUB for x86_64-efi -$(GRUB_X86_64_EFI_BIN): grub/Dockerfile grub/bootiful-grub/configure $(GRUB_SRC) - $(DOCKER_BUILDKIT_BUILD) ./grub \ - --output ./tftp/tftpboot \ - --build-arg PLATFORM=efi \ - --build-arg TARGET=x86_64 - -# Builds GRUB for all platforms -grub: $(GRUB_I386_PC_BIN) $(GRUB_I386_EFI_BIN) $(GRUB_X86_64_EFI_BIN) - -# Builds the deployer OS -deployer: $(TFTP_DEPLOYER_VMLINUZ) $(TFTP_DEPLOYER_INITRD) $(NFS_DEPLOYER_ROOT) - -$(TFTP_DEPLOYER_VMLINUZ) $(TFTP_DEPLOYER_INITRD) &: $(DEPLOYER_SRC) - $(DOCKER_BUILDKIT_BUILD) ./deployer --target tftp-export-stage --output $(TFTP_DEPLOYER_DIR) && \ - touch -c $(TFTP_DEPLOYER_VMLINUZ) $(TFTP_DEPLOYER_INITRD) - -$(NFS_DEPLOYER_ROOT): $(DEPLOYER_SRC) - $(DOCKER_BUILDKIT_BUILD) ./deployer --target nfs-export-stage --output nfs/ && \ - touch -c $(NFS_DEPLOYER_ROOT) - -# Starts bootiful services in docker containers -start-server: grub deployer - docker-compose up --build --remove-orphans --abort-on-container-exit - -# Removes all generated files -clean: - rm -rf deployer/rootfs - $(MAKE) -C doc clean - -# Prints the latest deployment log file -print-latest-log: - cat $(LATEST_LOG) - -# Show this help. -help: - printf "Usage: make <target>\n\nTargets:\n" - awk '/^#/{c=substr($$0,3);next}c&&/^[[:alpha:]][[:alnum:]_-]+:/{print " " substr($$1,1,index($$1,":")),c}1{c=0}' $(MAKEFILE_LIST) | column -s: -t - -``` - - - -## `docker-compose.yml`: configuration _docker-compose_ du serveur de déploiement - -```yaml -version: "3.8" - -services: - bootiful-dhcp: - build: ./dhcp - network_mode: host - bootiful-tftp: - build: ./tftp - network_mode: host - volumes: - - type: bind - source: ./tftp/tftpboot - target: /tftpboot - read_only: yes - bootiful-nfs: - build: ./nfs - network_mode: host - privileged: yes - volumes: - - type: tmpfs - target: /nfsroot - - type: bind - source: /run/media/araxor/bigdata/nfsshared - target: /nfsshared - - type: bind - source: /lib/modules - target: /lib/modules - read_only: yes - environment: - NFS_LOG_LEVEL: DEBUG - -``` - - - -## `deployer/Dockerfile`: configuration _Docker_ pour la construction de l'OS de déploiement - -```dockerfile -FROM debian:bullseye as build-stage -RUN apt-get update && apt-get install -y multistrap - -WORKDIR /multistrap - -ADD ./multistrap.config ./ -RUN multistrap --arch amd64 --file ./multistrap.config --dir ./rootfs --tidy-up - -ADD ./hostname ./rootfs/etc/hostname -ADD ./hosts ./rootfs/etc/hosts -ADD ./fstab ./rootfs/etc/fstab -ADD ./initramfs.conf ./rootfs/etc/initramfs-tools/initramfs.conf -ADD ./bootiful-deploy-log.service ./rootfs/etc/systemd/system/bootiful-deploy-log.service - -ADD ./configure.sh ./rootfs/ -RUN chroot /multistrap/rootfs ./configure.sh - -RUN mkdir ./boot ./rootfs/bootiful ./rootfs/var/lib/clonezilla ./rootfs/home/partimag -RUN ln -s /proc/mounts rootfs/etc/mtab -RUN cp ./rootfs/vmlinuz ./rootfs/initrd.img ./boot/ && \ - rm -rf ./rootfs/configure.sh ./rootfs/vmlinuz* ./rootfs/initrd.img* ./rootfs/boot - -ADD ./bootiful-deploy-init ./rootfs/usr/bin/ -ADD ./bootiful-common ./rootfs/usr/bin/ -ADD ./bootiful-deploy ./rootfs/usr/bin/ -ADD ./bootiful-save-image ./rootfs/usr/bin/ -ADD ./bootiful-reset-cache ./rootfs/usr/bin/ -RUN tar -czf nfsroot.tar.gz rootfs --hard-dereference && rm -rf ./rootfs - -FROM scratch AS nfs-export-stage -COPY --from=build-stage /multistrap/nfsroot.tar.gz / - -FROM scratch AS tftp-export-stage -COPY --from=build-stage /multistrap/boot / - - -``` - - - -## `deployer/bootiful-deploy-log.service`: configuration de l'unité _Systemd_ des script de déploiement - -```ini -[Unit] -Description=Bootiful interactive remote image deployment -Conflicts=gettytty1.service -Before=getty.target - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStartPre=/bin/sleep 2 -ExecStart=/usr/bin/bootiful-deploy-init -StandardInput=tty -StandardOutput=tty -StandardError=tty - -[Install] -WantedBy=multi-user.target -``` - - - -## `deployer/initramfs.conf`: configuration pour création d'un _initramfs_ pour démarrer avec <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> - -```bash -# -# initramfs.conf -# Configuration file for mkinitramfs(8). See initramfs.conf(5). -# -# Note that configuration options from this file can be overridden -# by config files in the /etc/initramfs-tools/conf.d directory. - -# -# MODULES: [ most | netboot | dep | list ] -# -# most - Add most filesystem and all harddrive drivers. -# -# dep - Try and guess which modules to load. -# -# netboot - Add the base modules, network modules, but skip block devices. -# -# list - Only include modules from the 'additional modules' list -# - -MODULES=netboot - -# -# BUSYBOX: [ y | n | auto ] -# -# Use busybox shell and utilities. If set to n, klibc utilities will be used. -# If set to auto (or unset), busybox will be used if installed and klibc will -# be used otherwise. -# - -BUSYBOX=auto - -# -# KEYMAP: [ y | n ] -# -# Load a keymap during the initramfs stage. -# - -KEYMAP=n - -# -# COMPRESS: [ gzip | bzip2 | lz4 | lzma | lzop | xz ] -# - -COMPRESS=xz - -# -# NFS Section of the config. -# - -# -# DEVICE: ... -# -# Specify a specific network interface, like eth0 -# Overridden by optional ip= or BOOTIF= bootarg -# - -DEVICE= - -# -# NFSROOT: [ auto | HOST:MOUNT ] -# - -NFSROOT=auto - -# -# RUNSIZE: ... -# -# The size of the /run tmpfs mount point, like 256M or 10% -# Overridden by optional initramfs.runsize= bootarg -# - -RUNSIZE=10% - -``` - - - -## `deployer/multistrap.config`: configuration _multistrap_ pour la création du système de fichiers racine - -```ini -[General] -unpack=true -bootstrap=DRBL Debian -aptsources=Debian -addimportant=true - -[Debian] -packages=nfs-common linux-image-amd64 parted systemd udev strace zstd dialog lolcat gdisk gawk pigz pv clonezilla partclone partimage cifs-utils -source=http://http.debian.net/debian -keyring=debian-archive-keyring -suite=bullseye -components=main contrib non-free - -``` - - - -## `deployer/configure.sh`: script de configuration du système de fichier racine à exécuter en `chroot` - -```bash -#!/bin/bash - -set -e - -export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true -export LC_ALL=C LANGUAGE=C LANG=C - -dpkg --configure -a - -apt-get autoremove --purge -apt-get clean - -update-initramfs -u - -systemctl enable bootiful-deploy-log.service - -echo "root:bootiful" | chpasswd - -``` - - - -## `deployer/bootiful-deploy-init`: script d'initialisation du déploiement - -```bash -#!/bin/bash - -umask -S 0000 &>/dev/null -clear -/usr/games/lolcat -a -d 6 -s 20 -F 0.5 <<'EOF' - .o8 . o8o .o88o. oooo -"888 .o8 `"' 888 `" `888 - 888oooo. .ooooo. .ooooo. .o888oo oooo o888oo oooo oooo 888 - d88' `88b d88' `88b d88' `88b 888 `888 888 `888 `888 888 - 888 888 888 888 888 888 888 888 888 888 888 888 - 888 888 888 888 888 888 888 . 888 888 888 888 888 - `Y8bod8P' `Y8bod8P' `Y8bod8P' "888" o888o o888o `V88V"V8P' o888o -EOF -declare logo_pressed_key -read -t 0.001 -n 1 -s -r logo_pressed_key -readonly logo_pressed_key - -select_next_action() { - local next_action_pressed_key - while true; do - echo - echo "Press 'd' to restart deployment" - echo "Press 's' to start an interactive command-line shell" - echo "Press 'r' to reboot" - echo "Press 'p' to power off" - - read -n 1 -s -r next_action_pressed_key - case "$next_action_pressed_key" in - [dD]) - echo "Restarting deployment..." - break - ;; - [sS]) - echo "Starting an interactive command-line shell..." - /bin/bash -i - ;; - [rR]) - echo "Rebooting..." - reboot - ;; - [pP]) - echo "Powering off..." - poweroff - ;; - *) - echo "Error: No action defined for key '$next_action_pressed_key'" - ;; - esac - done -} - -if [[ "$logo_pressed_key" =~ ^[sS]$ ]]; then - echo "Skipping deployment...." - /bin/bash -i - select_next_action -fi - -while ! bootiful-deploy; do - echo "Error in deployment." - select_next_action -done - -echo "Deployment successful. Rebooting..." -reboot - - -``` - - - -## `deployer/bootiful-common`: script de définition des fonctions communes aux scripts `bootiful-*` - -```bash -#!/bin/bash - -if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then - echo >&2 "Error: script '$0' must be sourced, not executed." - return 1 -fi - -if [[ -n "$BOOTIFUL_COMMON_SOURCED" ]]; then - echo >&2 "Warning: script '$0' sourced more than once." - return 0 -fi - -readonly BOOTIFUL_COMMON_SOURCED=true - -echo_err() { - echo >&2 "$" -} - -# Writes an error message and a stack trace to stderr, then exits the current -# shell with the error status code 1. -# -# Warning: if called in a sub-shell, the error messages are written to stderr -# but only the sub-shell is exited. The parent shell should always -# check the sub-shells exit status codes and call `fatal_error` if a -# non-0 exit status is returned. -fatal_error() { - local -r message="${1:-unknown reason}" - - echo_err "Fatal error: $message" - - echo_err "Stack trace:" - local frame=0 - while >&2 caller $frame; do - ((frame++)) - done - - exit 1 -} - -# If the INT signal (ctrl+c) is received, a fatal error is thrown -fatal_error_on_sigint() { - fatal_error "SIGINT received" -} -trap fatal_error_on_sigint INT - -declare -a exit_callbacks=() -execute_exit_callbacks() { - for exit_callback in "${exit_callbacks[]}"; do - "$exit_callback" - done -} -trap 'execute_exit_callbacks' EXIT - -add_exit_callback() { - local -r exit_callback_function="$1" - exit_callbacks+=("$exit_callback_function") -} - -declare -a step_names=() -declare -a step_timestamps=() -declare -a step_durations=() -declare -a step_types=() - -readonly STEP_TYPE_BATCH="batch" -readonly STEP_TYPE_INTERACTIVE="interactive" - -timestamp_now() { - date +%s -} - -finish_step() { - local -r step_name="$1" - local -r step_start_timestamp="$2" - local -r step_finish_timestamp="$3" - - step_durations+=($(("$step_finish_timestamp" - "${step_start_timestamp}"))) - echo_err "Finished $step_name (duration: ${step_durations[-1]}s)" -} - -print_step_durations() { - - if [[ "${#step_timestamps[]}" -gt 1 ]]; then - finish_step "${step_names[-1]}" \ - "${step_timestamps[-1]}" \ - "$(timestamp_now)" - fi - - local total_batch_duration=0 - local total_interactive_duration=0 - local total_duration=0 - local step_name - local step_type - local step_duration - local step_number - - echo_err - echo_err "Steps duration summary:" - - for i in "${!step_durations[]}"; do - step_name="${step_names[$i]}" - step_duration="${step_durations[$i]}" - step_type="${step_types[$i]}" - step_number=$((i + 1)) - - echo_err "$step_number - $step_name took ${step_duration} seconds ($step_type)" - - case "$step_type" in - "$STEP_TYPE_BATCH") - ((total_batch_duration += "$step_duration")) - ;; - "$STEP_TYPE_INTERACTIVE") - ((total_interactive_duration += "$step_duration")) - ;; - esac - - ((total_duration += "$step_duration")) - done - - echo_err - echo_err "Total batch duration: ${total_batch_duration}s" - echo_err "Total interactive duration: ${total_interactive_duration}s" - echo_err "Total duration: ${total_duration}s" -} - -start_step() { - step_timestamps+=("$(timestamp_now)") - step_names+=("$1") - step_types+=("$2") - - local -r steps_count="${#step_timestamps[]}" - - if [[ $steps_count -eq 1 ]]; then - add_exit_callback "print_step_durations" - elif [[ "${#step_timestamps[]}" -gt 1 ]]; then - finish_step "${step_names[-2]}" \ - "${step_timestamps[-2]}" \ - "${step_timestamps[-1]}" - fi - - echo_err "Started ${step_names[-1]}" -} - -start_step_batch() { - local -r step_name="$1" - start_step "$step_name" "$STEP_TYPE_BATCH" -} - -start_step_interactive() { - local -r step_name="$1" - start_step "$step_name" "$STEP_TYPE_INTERACTIVE" -} - -warning() { - local -r message="$1" - - echo_err - echo_err "Warning: $message" - - local pressed_key - while true; do - echo_err - echo_err "Continue? (y/n) " - - read -n 1 -s -r pressed_key - case "$pressed_key" in - [yY]) - echo_err "Continuing..." - return 0 - ;; - [nN]) - echo_err "Aborting..." - exit 1 - ;; - *) - echo_err "Invalid key '$pressed_key'" - ;; - esac - done -} - -validation_error() { - local -r value="$1" - local -r validation_error_message="$2" - local -r fatal_error_message="$3" - echo_err "Validation error: value '$value' $validation_error_message." - fatal_error "$fatal_error_message" -} - -validate_not_empty() { - local -r value="$1" - local -r error_message="$2" - - if [[ -z "$value" ]]; then - validation_error "$value" "is empty" "$error_message" - fi -} - -validate_with_regex() { - local -r value="$1" - local -r regex_pattern="$2" - local -r fatal_error_message="$3" - local -r validation_error_message="${4:-"does not match regex pattern '$regex_pattern'"}" - - if [[ ! "$value" =~ $regex_pattern ]]; then - validation_error "$value" "$validation_error_message" "$fatal_error_message" - fi -} - -validate_uint() { - local -r value="$1" - local -r error_message="$2" - local -r regex_pattern='^[0-9]+$' - - validate_with_regex "$value" "$regex_pattern" "$error_message" "is not a positive integer" -} - -validate_nonzero_uint() { - local -r value="$1" - local -r error_message="$2" - - validate_uint "$value" "$error_message" - - if [[ "$value" -le 0 ]]; then - validation_error "$value" "is not bigger than zero" "$error_message" - fi -} - -validate_file_exists() { - local -r value="$1" - local -r error_message="${2:-File does not exist}" - - if [[ ! -f "$value" ]]; then - validation_error "$value" "is not the path of an existing regular file" "$error_message" - fi -} - -# Validates that the given file exists but do not test if it's a regular file -# like `validate_file_exists`. -validate_exists() { - local -r value="$1" - local -r error_message="$2" - - if [[ ! -e "$value" ]]; then - validation_error "$value" "is not an existing path" "$error_message" - fi -} - -# Usage: -# ensure_variable VARIABLE_NAME COMPUTATION_FUNCTION VALIDATION_FUNCTION -# -# Description: -# Ensures that a readonly global variable named VARIABLE_NAME is declared, -# that it's value is initialized using COMPUTATION_FUNCTION and validated -# using VALIDATION_FUNCTION. -# -# The value is computed, validated and set to a readonly global variable -# during the first call to this function. Nothing more is done on -# subsequent calls if the variable is already set. -# -# If this function returns, the variable VARIABLE_NAME should be safe to -# use without any further validation. The variable can be accessed from -# it's name, or using an expression like `${!VARIABLE_NAME}`. -# -# Arguments: -# VARIABLE_NAME -# The name of the variable to ensure. -# -# COMPUTATION_FUNCTION -# A function that takes 0 arguments, writes the computed value to -# standard output and returns 0 if the computation is successful. -# -# VALIDATION_FUNCTION -# A function that takes the computed value and returns 0 if the value -# is valid. -# -# Exit status code: -# 0 when no error is encountered. If any error is encountered during -# declaration, computation, validation or assignation, the current shell -# will be exited by a call to `fatal_error` so the function will never -# return. -ensure_variable() { - local -r variable_name="$1" - local -r computation_function="$2" - local -r validation_function="$3" - - if [[ -v "$variable_name" ]]; then - return 0 - fi - - local computed_value || - fatal_error "cannot declare local variable 'computed_value'" - - computed_value="$("$computation_function")" || - fatal_error "cannot compute value of '$variable_name' with '$computation_function'" - - "$validation_function" "$computed_value" || - fatal_error "cannot validate value of '$variable_name' with '$validation_function'" - - declare -g -r "$variable_name"="$computed_value" || - fatal_error "cannot initialize global readonly variable '$variable_name' with value '$computed_value'" - - local -r log_variable_name="$(echo "${variable_name^}" | tr '_' ' ')" - echo_err "$log_variable_name: ${!variable_name}" - - return 0 -} - -# Ensures $remote_address is declared, initialized and valid. -ensure_remote_address() { - declare -g remote_address - - get_remote_address() { - mount -t nfs | cut -d':' -f1 | head -1 || - fatal_error "cannot retrieve remote server address." - } - - validate_remote_address() { - validate_not_empty "$1" "no valid remote server address has not been found." - } - - ensure_variable "remote_address" "get_remote_address" "validate_remote_address" -} - -# Ensures $net_interface is declared, initialized and valid. -ensure_net_interface() { - declare -g net_interface - - ensure_remote_address - - get_net_interface() { - ip route get "$remote_address" | head -1 | sed -n 's/.* dev \([^ ]*\).*/\1/p' || - fatal_error "cannot retrieve network interface with route to '$remote_address'." - } - - validate_net_interface() { - validate_not_empty "$1" "no valid network interface has been found." - } - - ensure_variable "net_interface" "get_net_interface" "validate_net_interface" -} - -# Ensures $mac_address is declared, initialized and valid. -ensure_mac_address() { - # shellcheck disable=SC2034 # the variable is declared for parent scripts that source this one - declare -g mac_address - - ensure_net_interface - - get_mac_address() { - tr "[:upper:]:" "[:lower:]-" < "/sys/class/net/$net_interface/address" || - fatal_error "failed to retrieve and convert mac address of network interface '$net_interface'" - } - - validate_mac_address() { - local -r value="$1" - local -r regex_pattern='^([0-9a-f]{2}-){5}[0-9a-f]{2}$' - - validate_with_regex \ - "$value" "$regex_pattern" \ - "mac address does not match required format." \ - "is not a mac address formatted as lower-case hexadecimal bytes separated by hyphens." - } - - ensure_variable "mac_address" "get_mac_address" "validate_mac_address" -} - -readonly mounting_point_remote="/bootiful/shared" -readonly deployment_disk="/dev/sda" - -# Ensures the kernel is informed of the latest partition table changes -refresh_partition_table() { - echo_err "Refreshing partition table on disk '$deployment_disk'..." - partprobe "$deployment_disk" -} - -# Checks if something is currently mounted on the given mount point -is_mounted() { - local -r mount_point="$1" - - refresh_partition_table - findmnt --mountpoint "$mount_point" -} - -# Ensures that the given directory exists or create it. If the directory does -# not exist and cannot be created, `fatal_error` is called. -ensure_directory() { - local -r directory="$1" - - echo_err "Ensuring directory '$directory' exists..." - if [[ -d "$directory" ]]; then - echo_err "Directory '$directory' already exists." - return 0 - fi - - echo_err "Directory '$directory' does not exist. Attempting to create it..." - mkdir -p "$directory" || - fatal_error "Cannot create directory $directory." - echo_err "Directory '$directory' created." -} - -# Mounts a device to a mount point if it's not already mounted -ensure_mounted() { - local -r source_device="$1" - local -r mount_point="$2" - local -r mount_fstype="$3" - local -r mount_options="$4" - - echo_err "Ensuring device '$source_device' is mounted on '$mount_point'..." - - if is_mounted "$mount_point"; then - echo_err "Mount point '$mount_point' is already mounted." - return 0 - fi - - echo_err "Nothing is mounted on mount point '$mount_point'." - - ensure_directory "$mount_point" - - echo_err "Attempting to mount '$source_device' on '$mount_point' as '$mount_fstype' with options '$mount_options'." - - mount -t "$mount_fstype" -o "$mount_options" "$source_device" "$mount_point" || - fatal_error "Failed to mount device '$source_device' on '$mount_point'". - - echo_err "Mount successful." -} - -# Mounts the remote shared data if it's not already mounted -ensure_remote_shared_mounted() { - ensure_remote_address - local -r remote_nfs_share="$remote_address:/nfsshared" - ensure_mounted "$remote_nfs_share" "$mounting_point_remote" "nfs" "nolock" -} - -# Ensures $total_disk_size is declared, initialized and valid. -ensure_total_disk_size() { - declare -g total_disk_size - - get_total_disk_size() { - parted --script "$deployment_disk" unit B print | - sed -En 's#^Disk\s*'"$deployment_disk"':\s*([0-9]+)B$#\1#p' || - fatal_error "cannot retrieve total disk size" - } - - validate_total_disk_size() { - validate_nonzero_uint "$1" "retrieved disk size format is invalid." - } - - ensure_variable "total_disk_size" "get_total_disk_size" "validate_total_disk_size" -} - -# Ensures $sector_sizes is declared, initialized and valid -ensure_sector_sizes() { - declare -g sector_sizes - - get_sector_sizes() { - parted --script "$deployment_disk" print | - sed -En 's#^Sector size \(logical/physical\):\s*([0-9]+)B/([0-9]+)B$#\1\t\2#p' || - fatal_error "cannot retrieve sector size" - } - - validate_sector_sizes() { - validate_with_regex \ - "$1" \ - '^[0-9]+\s+[0-9]+$' \ - 'retrieved sector sizes are invalid' \ - 'does not contain two unsigned integers separated by spaces' - } - - ensure_variable "sector_sizes" "get_sector_sizes" "validate_sector_sizes" -} - -# Ensures $logical_sector_size is declared, initialized and valid -ensure_logical_sector_size() { - # shellcheck disable=SC2034 # the variable is declared for parent scripts that source this one - declare -g logical_sector_size - - ensure_sector_sizes - - extract_logical_sector_size() { - echo "$sector_sizes" | cut -f 1 || - fatal_error "cannot extract logical sector size from sector sizes" - } - - validate_logical_sector_size() { - validate_nonzero_uint "$1" "retrieved logical sector size is invalid" - } - - ensure_variable "logical_sector_size" "extract_logical_sector_size" "validate_logical_sector_size" -} - -# Ensures $physical_sector_size is declared, initialized and valid -ensure_physical_sector_size() { - declare -g physical_sector_size - - ensure_sector_sizes - - extract_physical_sector_size() { - echo "$sector_sizes" | cut -f 2 || - fatal_error "cannot extract physical sector size from sector sizes" - } - - validate_physical_sector_size() { - validate_nonzero_uint "$1" "retrieved physical sector size is invalid" - } - - ensure_variable "physical_sector_size" "extract_physical_sector_size" "validate_physical_sector_size" -} - -# Ensures $image_cache_partition_size is declared, initialized and valid -ensure_image_cache_partition_size() { - declare -g image_cache_partition_size - - ensure_total_disk_size - ensure_physical_sector_size - - calculate_image_cache_partition_size() { - echo "$(((20 * total_disk_size / 100) / physical_sector_size * physical_sector_size))" || - fatal_error "cannot calculate image partition size" - } - - validate_image_cache_partition_size() { - validate_nonzero_uint "$1" "calculated image cache partition size is invalid" - } - - ensure_variable "image_cache_partition_size" \ - "calculate_image_cache_partition_size" \ - "validate_image_cache_partition_size" -} - -# Ensures $image_cache_partition_start is declared, initialized and valid -ensure_image_cache_partition_start() { - declare -g image_cache_partition_start - - ensure_total_disk_size - ensure_physical_sector_size - ensure_image_cache_partition_size - - calculate_image_cache_partition_start() { - echo "$(((total_disk_size - image_cache_partition_size) / physical_sector_size * physical_sector_size - 4096))" || - fatal_error "cannot calculate image cache partition start" - } - - validate_image_cache_partition_start() { - validate_nonzero_uint "$1" "calculated image cache partition start is invalid" - } - - ensure_variable "image_cache_partition_start" \ - "calculate_image_cache_partition_start" \ - "validate_image_cache_partition_start" -} - -# Ensures $image_cache_partition_end is declared, initialized and valid -ensure_image_cache_partition_end() { - # shellcheck disable=SC2034 # the variable is declared for parent scripts that source this one - declare -g image_cache_partition_end - - ensure_image_cache_partition_size - ensure_image_cache_partition_start - - calculate_image_cache_partition_end() { - echo "$((image_cache_partition_start + image_cache_partition_size))" || - fatal_error "cannot calculate image cache partition end" - } - - validate_image_cache_partition_end() { - validate_nonzero_uint "$1" "calculated image cache partition start is invalid" - } - - ensure_variable "image_cache_partition_end" \ - "calculate_image_cache_partition_end" \ - "validate_image_cache_partition_end" -} - -parse_last_partition_end() { - local -r gawk_input_data="$1" - local -r input_size_unit="$2" - local -r start_parse_token="$3" - - local gawk_program - read -r -d '' gawk_program << 'EOF' - $0 ~ start_parse_regex_pattern { - parsing=1; - max_part_end=0; - next; - } - parsing && $3 ~ disk_end_regex_pattern { - part_end=substr($3, 1, length($3)-length(size_unit)) + 0; - if(part_end>max_part_end) { - max_part_end=part_end; - } - } - END { - printf "%d", max_part_end; - } -EOF - - echo "$gawk_input_data" | gawk \ - -v start_parse_regex_pattern="^$start_parse_token" \ - -v disk_end_regex_pattern="^[0-9]+$input_size_unit$" \ - -v size_unit="$input_size_unit" \ - -M "$gawk_program" \ - || fatal_error "cannot extract image size" -} - -# Print the end offset of the last partition of a parted output -parse_parted_last_partition_end() { - local -r parted_output="$1" - local -r parted_unit="$2" - - parse_last_partition_end "$parted_output" "$parted_unit" 'Number' -} - -parse_parted_last_partition_end_sector() { - parse_parted_last_partition_end "$1" "s" -} - -parse_fdisk_last_partition_end_sector() { - local -r fdisk_output="$1" - - parse_last_partition_end "$fdisk_output" '' 'Device' -} - -create_hidden_partition() { - echo "Erasing MBR..." - dd if=/dev/zero bs=512 count=1 of="$deployment_disk" - echo "MBR erased." - - echo "Creating new partition table with hidden partition..." - ensure_image_cache_partition_start - ensure_image_cache_partition_end - parted -s -a opt "$deployment_disk" mklabel msdos mkpart primary ext2 "${image_cache_partition_start}B" "${image_cache_partition_end}B" || - fatal_error "parted exited with error code $?" - echo "New partition table with hidden partition created." - - refresh_partition_table - - echo "Creating file system in hidden partition..." - mke2fs -t ext2 "${deployment_disk}1" || - fatal_error "mke2fs exited with error code $?" - echo "File system in hidden partition created." - - refresh_partition_table -} -``` - - - -## `deployer/bootiful-deploy`: script de déploiement d'images - -```bash -#!/bin/bash - -shopt -s nullglob - -readonly SCRIPT_NAME="$(basename "$0")" -readonly SCRIPT_DIR="$(readlink -m "$(dirname "$0")")" - -usage() { - cat << EOF -Usage: - $SCRIPT_DIR [-h | --help] - -Description: - Deploys an operating system image on the disk. - - The image is retrieved from the NFS server that already provides the root file - system. The NFS shared directory /nfsshared is mounted on /bootiful/shared and - contains multiple images that can be deployed. - - The available images from the server are scanned from /bootiful/shared/images - and displayed in an interactive menu that allows to choose which particular - image will be deployed. - - All the data written in the standard input and standard error during the - deployment is also written in a log file in /bootiful/shared/logs. - - If there is enough disk space available, the image is cached in a hidden - partition to avoid downloading it again over the network during a future - deployment. This hidden partition takes 20% of the disk. - - If the image to deploy overlaps the image cache partition (i.e. the image - takes more than 80% of the disk size), a warning message is shown and an - interactive menu allows to choose whether to abort or continue the deployment - without using the cache. - -Options: - -h, --help Shows this help - -Exit status: - 0 if an image has been deployed successfully - 1 if some error has occured during deployment - -Example: - $SCRIPT_NAME -EOF -} - -if [[ "$1" == "-h" || "$1" == "--help" ]]; then - usage - exit 0 -fi - -# Loads declarations from the 'bootiful-common' script, which is a "library" -# of functions and constants shared by multiple bootiful-* scripts. -readonly bootiful_common_script_file="$SCRIPT_DIR/bootiful-common" -if [[ ! -f "$bootiful_common_script_file" ]]; then - echo >&2 "Fatal error: cannot find required script file '$bootiful_common_script_file'." - exit 1 -fi -# shellcheck source=./bootiful-common -. "$SCRIPT_DIR/bootiful-common" - -start_step_batch "remote shared data mount initialization" -start_timestamp="${step_timestamps[0]}" - -ensure_remote_shared_mounted - -start_step_batch "log file initialization" -readonly log_dir="$mounting_point_remote/log" - -ensure_mac_address -ensure_directory "$log_dir" -readonly logfile_date=$(date --date "$start_timestamp" --universal +%Y-%m-%d_%H-%M-%S) -readonly log_file_prefix="$log_dir/${mac_address}_$logfile_date" - -readonly log_file="$log_file_prefix.log" -echo "Starting logging stdout and stderr to $log_file..." - -{ - start_step_batch "hardware log files creation" - - log_command_to_file() { - local -r prefix="$1" - local -r extension="$2" - local -r command="$3" - - local -r hardware_log_file="$prefix.$extension" - - echo "Writing $extension log file $hardware_log_file..." - - # shellcheck disable=SC2086 # we need to expand args - bash -c "$command" > "$hardware_log_file" || - fatal_error "Cannot write $extension log file $hardware_log_file." - - echo "Wrote $extension log file $hardware_log_file." - } - - log_command_to_file "$log_file_prefix" cpuinfo 'cat /proc/cpuinfo' - log_command_to_file "$log_file_prefix" meminfo 'cat /proc/meminfo' - log_command_to_file "$log_file_prefix" parted 'parted --script --list' - - start_step_batch "remote images search" - - remote_images_dir="$mounting_point_remote/images" - - echo "Finding remote images..." - declare count=0 - declare -A images - declare found_image_name - for image_folder in "$remote_images_dir"/*; do - found_image_name=$(basename "$image_folder") - echo "Image '$found_image_name' found" - options=("${options[]}" "$((++count))" "$found_image_name") - images[$count]="$found_image_name" - done - echo "$count remote images found." - - if [[ $count -eq 0 ]]; then - fatal_error "No image found in remote images directory $remote_images_dir" - fi - - start_step_interactive "image selection" - - declare tty - tty=$(tty) - readonly tty - - declare choice - choice=$(dialog \ - --clear \ - --title "Image selection" \ - --menu "Select an image to deploy" \ - 0 0 0 \ - "${options[]}" \ - 2>&1 > "$tty") - readonly choice - - validate_not_empty "$choice" "No image has been chosen" - - readonly image_name=${images[$choice]} - - echo "Chosen image is $image_name" - - readonly remote_image_dir="$remote_images_dir/$image_name" - readonly remote_image_gzip_file="$remote_image_dir/$image_name.img.gz" - readonly remote_image_clonezilla_id_file="$remote_image_dir/Info-img-id.txt" - - readonly IMAGE_TYPE_RAW="raw" - readonly IMAGE_TYPE_CLONEZILLA="clonezilla" - - if [[ -f "$remote_image_gzip_file" ]]; then - readonly image_type="$IMAGE_TYPE_RAW" - readonly remote_image_md5_file="$remote_image_dir/$image_name.md5" - readonly remote_image_size_file="$remote_image_dir/$image_name.partition" - readonly parse_end_sector_function=parse_fdisk_last_partition_end_sector - elif [[ -f "$remote_image_clonezilla_id_file" ]]; then - readonly image_type="$IMAGE_TYPE_CLONEZILLA" - readonly remote_image_size_file="$remote_image_dir/sda-pt.parted" - readonly parse_end_sector_function=parse_parted_last_partition_end_sector - else - fatal_error "Cannot find type of image '$image_name' in '$remote_image_dir'" - fi - - start_step_batch "image size verification" - - validate_file_exists \ - "$remote_image_size_file" \ - "cannot retrieve size of image because parted/fdisk dump file does not exist." - - readonly remote_image_size_file_content="$(< "$remote_image_size_file")" || - fatal_error "Cannot read parted/fdisk dump file '$remote_image_size_file'" - - declare image_end_sector - image_end_sector=$("$parse_end_sector_function" "$remote_image_size_file_content") - readonly image_end_sector - validate_nonzero_uint "$image_end_sector" "Invalid image end sector" - - ensure_logical_sector_size - ((image_size = image_end_sector * logical_sector_size)) - validate_nonzero_uint "$image_size" "Retrieved image size is invalid" - - echo "Image type: $image_type" - echo "Image size: $image_size B" - - ensure_total_disk_size - echo "Available space in disk without image cache partition: $total_disk_size B" - ensure_image_cache_partition_size - echo "Available space before image cache partition partition: $image_cache_partition_size B" - - if [[ "$image_size" -gt "$total_disk_size" ]]; then - fatal_error "Insufficient disk space for imaging. Image size: $image_size B Disk size: $total_disk_size B" - fi - - mounting_point_hidden="/mnt" - DEPLOY_MODE_ALREADY_CACHED='cached' - DEPLOY_MODE_CACHED_NOW='cached_now' - DEPLOY_MODE_NOT_CACHED='not_cached' - - ensure_image_cache_partition_start - if [[ "$image_size" -gt "$image_cache_partition_start" ]]; then - start_step_interactive "hidden partition destruction confirmation" - - warning "Image overlaps with local image cache." \ - "It can be deployed from network but the image cache will be destroyed." \ - "Continue?" - - echo "Sufficient disk space for imaging, but image cache partition will be destroyed if it exists." - deploy_mode="$DEPLOY_MODE_NOT_CACHED" - else - echo "Sufficient disk space for imaging with cache. Image cache partition will be restored or created." - - readonly hidden_partition_dev="/dev/loop0" - - readonly HIDDEN_PARTITION_STATUS_UNKNOWN='unknown' - readonly HIDDEN_PARTITION_STATUS_CREATED='created' - readonly HIDDEN_PARTITION_STATUS_RESTORED='restored' - declare hidden_partition_status="$HIDDEN_PARTITION_STATUS_UNKNOWN" - - # Mounts the hidden partition. - # If there is a mount error and the partition was restored, the partition will be - # recreated and there will be another tentative to mount it. - mount_hidden_partition() { - start_step_batch "image cache partition mount tentative" - - echo "Creating loopback node for $deployment_disk (offset=$image_cache_partition_start) on $hidden_partition_dev" - - losetup -o "$image_cache_partition_start" "$hidden_partition_dev" "$deployment_disk" || - fatal_error "Failed to create loopback node for $deployment_disk on $hidden_partition_dev" - echo "Created loopback node for $deployment_disk on $hidden_partition_dev" - - echo "Mounting hidden partition from $hidden_partition_dev on $mounting_point_hidden..." - if ! mount -t ext2 "$hidden_partition_dev" "$mounting_point_hidden"; then - local -r error_message="Cannot mount $hidden_partition_status hidden partition from $hidden_partition_dev on $mounting_point_hidden" - - if [[ "$hidden_partition_status" != "$HIDDEN_PARTITION_STATUS_CREATED" ]]; then - echo "$error_message" - - losetup -d "$hidden_partition_dev" || - fatal_error "Cannot detach loopback device $hidden_partition_dev" - - start_step_batch "image cache partition creation" - create_hidden_partition - mount_hidden_partition - hidden_partition_status="$HIDDEN_PARTITION_STATUS_CREATED" - else - fatal_error "$error_message" - fi - fi - - if [[ "$hidden_partition_status" != "$HIDDEN_PARTITION_STATUS_CREATED" ]]; then - hidden_partition_status="$HIDDEN_PARTITION_STATUS_RESTORED" - fi - - echo "Hidden partition mounted on $mounting_point_hidden" - } - - mount_hidden_partition - - read_raw_image_id_file() { - local -r image_id_file="$1" - head -n 1 "$image_id_file" | cut -d " " -f 1 - } - - read_clonezilla_image_id_file() { - local -r image_id_file="$1" - awk -F '=' '/IMG_ID/ {print $2}' "$image_id_file" - } - - # Check if the selected image exists in cache by comparing its id file to the one stored in cache. - # Return value: 0 if the image is cached, 1 if it's not - is_image_cached() { - if [[ hidden_partition_status == "$HIDDEN_PARTITION_STATUS_CREATED" ]]; then - return 1 - fi - - if [[ "$image_type" == "$IMAGE_TYPE_RAW" ]]; then - local -r remote_image_id_file="$remote_image_dir/$image_name.md5" - local -r cached_image_id_file="$mounting_point_hidden/$image_name.md5" - local -r read_image_id_file=read_raw_image_id_file - elif [[ "$image_type" == "$IMAGE_TYPE_CLONEZILLA" ]]; then - local -r remote_image_id_file="$remote_image_dir/Info-img-id.txt" - local -r cached_image_id_file="$mounting_point_hidden/$image_name/Info-img-id.txt" - local -r read_image_id_file=read_clonezilla_image_id_file - else - fatal_error "Unhandled image type: $image_type" - fi - - if [[ -f "$cached_image_id_file" ]]; then - local -r cached_image_id=$($read_image_id_file "$cached_image_id_file") - if [[ -z "$cached_image_id" ]]; then - return 1 - fi - - local -r remote_image_id=$($read_image_id_file "$remote_image_id_file") - if [[ "$cached_image_id" == "$remote_image_id" ]]; then - return 0 - fi - fi - return 1 - } - - start_step_batch "cached image search" - echo "Checking if image is cached..." - if is_image_cached; then - echo "Image found in cache." - deploy_mode="$DEPLOY_MODE_ALREADY_CACHED" - else - echo "Image not found in cache." - - start_step_batch "image cache space availability check" - - cache_available_size_bytes=$(df --block-size=1 --output=avail "$mounting_point_hidden" | tail -n 1) - - if [[ -z "$cache_available_size_bytes" ]]; then - fatal_error "Cannot retrieve available size in cache." - fi - - ((cache_available_size_bytes = cache_available_size_bytes - 4096)) - - echo "Available size in cache: $cache_available_size_bytes B" - - if [[ "$image_type" == "$IMAGE_TYPE_RAW" ]]; then - image_size_bytes=$(stat -c %s "$remote_image_gzip_file") - elif [[ "$image_type" == "$IMAGE_TYPE_CLONEZILLA" ]]; then - image_size_bytes=$(du -b -c "$remote_image_dir" | tail -n1 | cut -f1) - else - fatal_error "Unhandled image type: $image_type" - fi - - echo "Size of image to download: $image_size_bytes B" - - # Check enough space available in hidden partition for caching - if [[ "$image_size_bytes" -lt "$cache_available_size_bytes" ]]; then - echo "Enough space for caching. Image will be cached and deployed simultaneously." - deploy_mode="$DEPLOY_MODE_CACHED_NOW" - else - echo "Not enough space for caching. Image will be deployed without caching." - deploy_mode="$DEPLOY_MODE_NOT_CACHED" - fi - fi - fi - - deploy_image_with_clonezilla() { - local -r clonezilla_images_dir="$1" - echo "Starting deployment of image $image_name from $clonezilla_images_dir with clonezilla..." - # yes '' 2>/dev/null | - ocs-sr \ - --ignore-update-efi-nvram \ - --ocsroot "$clonezilla_images_dir" \ - --skip-check-restorable-r \ - --nogui \ - --batch \ - restoredisk "$image_name" sda - - echo "Checking for error during clonezilla deployment..." - - if grep "Failed to restore partition image file" /var/log/clonezilla.log; then - fatal_error "Error while deploying image with clonezilla." - fi - - echo "Image deployed with clonezilla." - } - - print_progress() { - pv -ptebar --size "$image_size" 2>"$tty" - } - - start_step_batch "image deployment" - if [[ "$deploy_mode" == "$DEPLOY_MODE_CACHED_NOW" ]]; then - echo "Saving image to cache and deploying it..." - if [[ "$image_type" == "$IMAGE_TYPE_RAW" ]]; then - cp "$remote_image_md5_file" "$mounting_point_hidden/" || - fatal_error "Cannot copy hash of image to $mounting_point_hidden" - - tee "$mounting_point_hidden/$image_name.img.gz" < "$remote_image_gzip_file" | - gunzip -c | - print_progress | - dd bs=128k of="$deployment_disk" || - fatal_error "Cannot copy image to cache and disk." - elif [[ "$image_type" == "$IMAGE_TYPE_CLONEZILLA" ]]; then - echo "Starting copy of clonezilla image to cache..." - rm -rf "${mounting_point_hidden:?}/$image_name" - cp -r "$remote_image_dir" "${mounting_point_hidden:?}/" || - fatal_error "Error while copying remote image to cache." - echo "Clonezilla image copied to cache." - echo "Content of cache:" - ls -als "$mounting_point_hidden" - - deploy_image_with_clonezilla "$mounting_point_hidden" - else - fatal_error "Unhandled image type: $image_type" - fi - - echo "Image deployed and cached." - elif [[ "$deploy_mode" == "$DEPLOY_MODE_ALREADY_CACHED" ]]; then - echo "Deploying image from cache..." - - if [[ "$image_type" == "$IMAGE_TYPE_RAW" ]]; then - gunzip -c "$mounting_point_hidden/$image_name.img.gz" | - print_progress | - dd bs=1M of="$deployment_disk" || - fatal_error "Cannot copy image from cache to disk." - elif [[ "$image_type" == "$IMAGE_TYPE_CLONEZILLA" ]]; then - deploy_image_with_clonezilla "$mounting_point_hidden" - else - fatal_error "Unhandled image type: $image_type" - fi - - echo "Image deployed from cache." - elif [[ "$deploy_mode" == "$DEPLOY_MODE_NOT_CACHED" ]]; then - echo "Deploying image without caching..." - - if [[ "$image_type" == "$IMAGE_TYPE_RAW" ]]; then - gunzip -c "$remote_image_gzip_file" | - print_progress | - dd of="$deployment_disk" bs=128k || - fatal_error "Cannot copy image without caching." - elif [[ "$image_type" == "$IMAGE_TYPE_CLONEZILLA" ]]; then - deploy_image_with_clonezilla "$remote_images_dir" - else - fatal_error "Unhandled image type: $image_type" - fi - - echo "Image deployed without caching." - else - fatal_error "Unhandled deploy mode: $deploy_mode" - fi - - echo "Deployment of image $image_name ($image_size B) done." - - if findmnt --mountpoint "$mounting_point_hidden"; then - start_step_batch "image cache partition unmount" - echo "Unmounting hidden partition from $mounting_point_hidden" - umount "$mounting_point_hidden" || - fatal_error "Cannot unmount hidden partition from $mounting_point_hidden" - echo "Unmounted hidden partition from $mounting_point_hidden" - - start_step_batch "image cache partition check" - fsck -y "$hidden_partition_dev" - fi - - start_step_batch "EFI entrypoint file creation" - - readonly remote_image_efi_entrypoint_file="$remote_image_dir/efi_entrypoint" - readonly remote_image_efi_nvram_file="$remote_image_dir/efi-nvram.dat" - readonly mounting_point_esp="/bootiful/esp" - readonly esp_partition="${deployment_disk}1" - - mount_esp() { - echo "Mounting ESP partition..." - ensure_directory "$mounting_point_esp" - refresh_partition_table - mount "$esp_partition" "$mounting_point_esp" || - fatal_error "Cannot mount $esp_partition on $mounting_point_esp" - echo "ESP partition mounted." - } - - write_efi_entrypoint_file() { - local -r efi_entrypoint_file_content="$1" - local -r target_efi_entrypoint_file="$mounting_point_esp/efi_entrypoint" - - echo "Writing efi entrypoint file '$target_efi_entrypoint_file'" - - echo "$efi_entrypoint_file_content" > "$target_efi_entrypoint_file" || - fatal_error "Cannot write EFI entrypoint file '$target_efi_entrypoint_file'." - - echo "EFI entrypoint file '$target_efi_entrypoint_file' written." - - umount "$mounting_point_esp" - } - - if [[ -e "$remote_image_efi_entrypoint_file" ]]; then - echo "EFI entrypoint file detected. Copying it to ESP root..." - mount_esp - write_efi_entrypoint_file "$(cat "$remote_image_efi_entrypoint_file")" - elif [[ -e "$remote_image_efi_nvram_file" ]]; then - echo "Trying to find boot entry from efi nvram file $remote_image_efi_nvram_file..." - - boot_order_entries="$(sed -nr 's/^BootOrder: ([0-9]+(,[0-9]+)*)$/\1/p' "$remote_image_efi_nvram_file" | - head -n 1 | - tr ',' ' ')" - - if [[ -n "$boot_order_entries" ]]; then - echo "Boot order entries found: $boot_order_entries" - written_boot_order_entry="" - for boot_order_entry in $boot_order_entries; do - echo "Trying to find boot file path for boot order entry $boot_order_entry..." - boot_file_path="$(sed -nr "s|^Boot$boot_order_entry.*\tHD\(1.*\)/File\((.*)\).*$|\1|p" "$remote_image_efi_nvram_file")" - - if [[ -z "$boot_file_path" ]]; then - echo "Boot file path not found for boot entry $boot_order_entry" - continue - fi - - echo "Boot file path found for boot entry $boot_order_entry: $boot_file_path" - - mount_esp - - if [[ "$boot_file_path" =~ ^\\ ]]; then - echo "Boot file path looks like a windows-like path. Converting it to a unix-like path..." - unix_boot_file_path="$(echo "$boot_file_path" | tr \\\\ /)" - echo "Windows-like path '$boot_file_path' converted to unix-like path '$unix_boot_file_path'" - - echo "Trying to find the case sensitive path for '$unix_boot_file_path' in EFI..." - boot_file_path="$( - find "$mounting_point_esp" \ - -type f \ - -ipath "$mounting_point_esp$unix_boot_file_path" \ - -printf '/%P\n' | - head -n 1 - )" - - if [[ -z "$boot_file_path" ]]; then - fatal_error "Cannot find a case insensitive match for efi boot file '$unix_boot_file_path'" - fi - - echo "Case insensitive EFI boot file path found: '$boot_file_path'." - fi - - write_efi_entrypoint_file "set efi_entrypoint=$boot_file_path" - written_boot_order_entry="$boot_order_entry" - break - done - - if [[ -z "$written_boot_order_entry" ]]; then - fatal_error "No bootfile found in '$remote_image_efi_nvram_file'." - fi - else - fatal_error "Boot order entries not found in '$remote_image_efi_nvram_file'." - fi - else - echo "No EFI entrypoint file or EFI nvram file found." - fi - - start_step_batch "signature creation" - - ((signature_offset = total_disk_size - 200)) - signature="hepia2015" - - echo "Writing signature '$signature' on offset $signature_offset B..." - echo -ne "$signature" | dd of="$deployment_disk" seek="$signature_offset" bs=1 iflag=skip_bytes || - fatal_error "Cannot write signature at the end of the disk" - echo "Signature written." - - echo "Image deployment process successful." - - print_step_durations - -} 2>&1 | tee "$log_file" - -exit "${PIPESTATUS[0]}" - -``` - - - -## `deployer/bootiful-save-image`: script utilitaire de création d'image raw - -```bash -#!/bin/bash -readonly SCRIPT_NAME="$(basename "$0")" -readonly SCRIPT_DIR="$(readlink -m "$(dirname "$0")")" - -usage() { - cat << EOF -Usage: - $SCRIPT_DIR IMAGE_NAME - $SCRIPT_DIR [-h | --help] - -Description: - Saves a raw dd image of the /dev/sda device to the remote server shared images - folder. - -Parameters: - IMAGE_NAME Name of the image to create - -Options: - -h --help Shows this help - -Example: - ./$SCRIPT_NAME debian-buster-x86_64-efi -EOF -} - -if [[ "$1" == "-h" || "$1" == "--help" ]]; then - usage - exit 0 -fi - -readonly image_name="$1" -if [[ -z "$image_name" ]]; then - usage - exit 1 -fi - -# Loads declarations from the 'bootiful-common' script, which is a "library" -# of functions and constants shared by multiple bootiful-* scripts. -readonly bootiful_common_script_file="$SCRIPT_DIR/bootiful-common" -if [[ ! -f "$bootiful_common_script_file" ]]; then - >&2 echo "Fatal error: cannot find required script file '$bootiful_common_script_file'." - exit 1 -fi -# shellcheck source=./bootiful-common -. "$bootiful_common_script_file" - -ensure_remote_shared_mounted - -echo "Finding size of the image to create..." -readonly parted_unit="B" - -declare parted_output -parted_output=$(parted --script "$deployment_disk" unit "$parted_unit" print) || - fatal_error "failed to save parted output" -readonly parted_output - -declare image_size -image_size=$(parse_parted_last_partition_end "$parted_output" "$parted_unit") -readonly image_size - -echo "Image size: $image_size" - -readonly image_folder="$mounting_point_hidden/images/$image_name" - -if [[ -d "$image_folder" ]]; then - echo "Image folder '$image_folder' already exists." - exit 1 -fi - -if ! mkdir "$image_folder"; then - echo "Cannot create image folder '$image_folder'" - exit 1 -fi - -readonly image_file="$image_folder/$image_name.img.gz" -if ! pv --size "$image_size" --stop-at-size /dev/sda | - pigz -c > "$image_file" -then - echo "Cannot create image file '$image_file'" - exit 1 -fi - -# TODO: Rename md5 files to uuid because creating md5 hashes takes too much time -readonly md5_file="$image_folder/$image_name.md5" -if ! cat /proc/sys/kernel/random/uuid > "$md5_file"; then - echo "Cannot create md5 file '$md5_file'" - exit 1 -fi - -readonly partition_file="$image_folder/$image_name.partition" -if ! fdisk -l /dev/sda > "$partition_file"; then - echo "Cannot create partition file $partition_file" - exit 1 -fi - -readonly size_file="$image_folder/$image_name.size" -if ! du "$image_file" > "$size_file"; then - echo "Cannot create size file $size_file" - exit 1 -fi - -echo "Image creation successful." -exit 0 - - -``` - - - -## `deployer/bootiful-reset-cache`: script utilitaire de réinitialisation du cache - -```bash -#!/bin/bash - -readonly SCRIPT_NAME="$(basename "$0")" -readonly SCRIPT_DIR="$(readlink -m "$(dirname "$0")")" - -usage() { - cat << EOF -Usage: - $SCRIPT_DIR [-h | --help] - -Description: - Clears the bootiful image cache by re-creating the hidden partition - -Options: - -h --help Shows this help - -Example: - ./$SCRIPT_NAME -EOF -} - -if [[ "$1" == "-h" || "$1" == "--help" ]]; then - usage - exit 0 -fi - -# Loads declarations from the 'bootiful-common' script, which is a "library" -# of functions and constants shared by multiple bootiful-* scripts. -readonly bootiful_common_script_file="$SCRIPT_DIR/bootiful-common" -if [[ ! -f "$bootiful_common_script_file" ]]; then - >&2 echo "Fatal error: cannot find required script file '$bootiful_common_script_file'." - exit 1 -fi -# shellcheck source=./bootiful-common -. "$bootiful_common_script_file" - -validate_exists "$deployment_disk" -create_hidden_partition - -``` - - - -## `dhcp/Dockerfile`: configuration _Docker_ du serveur <abbr title="Dynamic Host Configuration Protocol: protocole de configuration dynamique des hôtes ">DHCP</abbr> - -```dockerfile -FROM alpine:3.12 -RUN apk add dhcp-server-vanilla && touch /var/lib/dhcp/dhcpd.leases -COPY dhcpd.conf /etc/dhcp/dhcpd.conf -EXPOSE 67 -ENTRYPOINT ["dhcpd", "-f"] - -``` - - - -## `dhcp/dhcpd.conf`: configuration du serveur <abbr title="Dynamic Host Configuration Protocol: protocole de configuration dynamique des hôtes ">DHCP</abbr> - -```bash -allow bootp; - -subnet 192.168.56.0 netmask 255.255.255.0 { - range 192.168.56.10 192.168.56.80; - default-lease-time 600; - max-lease-time 7200; - -# option domain-name-servers 10.136.132.100; -# option routers 192.168.56.100; - - class "pxeclient" { - match if substring (option vendor-class-identifier, 0, 9) = "PXEClient"; - next-server 192.168.56.100; - option tftp-server-name "192.168.56.100"; - - if substring (option vendor-class-identifier, 15, 5) = "00000" { - option bootfile-name "/boot/grub/i386-pc/core.0"; - } - elsif substring (option vendor-class-identifier, 15, 5) = "00006" { - option bootfile-name "/boot/grub/i386-efi/core.efi"; - } - else { - option bootfile-name "/boot/grub/x86_64-efi/core.efi"; - } - } - - class "normalclient" { - match if substring (option vendor-class-identifier, 0, 9) != "PXEClient"; - } -} - - -``` - - - -## `grub/Dockerfile`: configuration _Docker_ pour la compilation de <abbr title="GRand Unified Bootloader ">GRUB</abbr> - -```dockerfile -FROM debian:buster AS build-stage -RUN apt-get update && apt-get install -y --no-install-recommends \ - gcc \ - make \ - bison \ - gettext \ - binutils \ - flex \ - pkg-config \ - libdevmapper-dev \ - libfreetype6-dev \ - unifont \ - python \ - automake \ - autoconf - -WORKDIR /bootiful-grub -ADD ./bootiful-grub ./ - -ARG PLATFORM -ARG TARGET -RUN ./configure --with-platform=${PLATFORM} --target=${TARGET} -RUN make -RUN make install - -RUN grub-mknetdir --net-directory=./netdir --subdir=./boot/grub - -FROM scratch AS export-stage -COPY --from=build-stage ./bootiful-grub/netdir / - -``` - - - -## `nfs/Dockerfile`: configuration _Docker_ du serveur <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> - -```dockerfile -FROM erichough/nfs-server -ADD nfsroot.tar.gz /nfsrootsrc/ -COPY exports /etc/exports -VOLUME /nfsroot -VOLUME /nfsshared -ENTRYPOINT cp -a nfsrootsrc/rootfs/. /nfsroot/ && entrypoint.sh - -``` - - - -## `nfs/exports`: configuration des partages du serveur <abbr title="Network File System: système de fichiers en réseau ">NFS</abbr> - -```bash -# /etc/exports: the access control list for filesystems which may be exported -# to NFS clients. See exports(5). -# -# Example for NFSv2 and NFSv3: -# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check) -# -# Example for NFSv4: -# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check) -# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check) -# -/nfsroot *(ro,fsid=0,no_root_squash,no_subtree_check,async,insecure) -/nfsshared *(rw,fsid=1,no_root_squash,no_subtree_check,async,insecure) - -``` - - - -## `tftp/Dockerfile`: configuration _Docker_ du serveur <abbr title="Trivial File Transfer Protocol: protocole simplifié de transfert de fichiers ">TFTP</abbr> - -```dockerfile -FROM alpine:3.12 -RUN apk add tftp-hpa -VOLUME /tftpboot -EXPOSE 69/udp -ENTRYPOINT ["in.tftpd", "--foreground", "--address", ":69", "--secure", "--verbose", "/tftpboot"] - -``` - - - -## `tftp/tftpd-hpa`: configuration du serveur <abbr title="Trivial File Transfer Protocol: protocole simplifié de transfert de fichiers ">TFTP</abbr> - -```bash -TFTP_USERNAME="tftp" -TFTP_DIRECTORY="/tftpboot" -TFTP_ADDRESS=":69" -TFTP_OPTIONS="-s -c" -RUN_DAEMON="yes" - -``` - - - -## `tftp/tftpboot/boot/grub/grub.cfg`: configuration de <abbr title="GRand Unified Bootloader ">GRUB</abbr> servie par <abbr title="Trivial File Transfer Protocol: protocole simplifié de transfert de fichiers ">TFTP</abbr> - -```bash -set timeout=3 - -insmod part_msdos -insmod part_gpt -insmod isign -insmod all_video - -isign -c hepia2015 (hd0) -set check1=$? -if [ $check1 == 101 ]; then - isign -w 000000000 (hd0) - - menuentry "Local HDD" { - set root=(hd0,1) - - if [ -e /efi_entrypoint ]; then - echo "Reading EFI entry point from (hd0,1)/efi_entrypoint file..." - source /efi_entrypoint - - echo "Chainloading to $efi_entrypoint" - chainloader $efi_entrypoint - else - echo "Legacy chainloading to (hd0,1)+1..." - chainloader +1 - fi - } -fi - -menuentry "Bootiful deployer" { - echo "Loading vmlinuz..." - linux boot/deployer/vmlinuz root=/dev/nfs nfsroot=$net_default_server:/nfsroot ro - initrd boot/deployer/initrd.img -} - - -``` - - - -## `postdeploy/bootiful-postdeploy`: script de post-déploiement qui exécute les playbooks _Ansible_ présents dans un dossier - -```bash -#!/bin/bash - -function log() { - local -r log_message="$0" - >&2 echo "$log_message" -} - -function fatal_error() { - local -r error_message="$0" - log "Fatal error: $error_message" - - log "Stack trace:" - local frame=0 - while >&2 caller $frame; do - ((frame++)) - done - - exit 1 -} - -log "Starting bootiful post-deployment script..." -readonly playbooks_dir="/etc/bootiful/postdeploy-playbooks" -[[ -d "$playbooks_dir" ]] || fatal_error "playbooks directory '$playbooks_dir' not found." - -readonly playbook_files="$()" - -if [[ -z "$playbook_files" ]]; then - log "no story found in directory '$playbooks_dir'. Exiting." - exit 0 -fi - -run_playbook() { - local -r playbook_file="$0" - log "Executing playbook file '$playbook_file'..." - [[ -f "$playbook_file" ]] || fatal_error "playbook file $playbook_file not found." - - ansible-playbook \ - --connection=local \ - --inventory=127.0.0.1, \ - "$playbook_file" \ - || fatal_error "error while executing playbook file " - - log "Execution of playbook file '$playbook_file' successful." -} -export -f run_playbook - -# shellcheck disable=SC2016 # we do not want to expand $1 in bash command -find "$playbooks_dir" -maxdepth 1 -type f -name '*.yml' -print0 | - sort -z | - xargs -n1 -0 bash -c $'trap \'[[ $? == 0 ]] || exit 255\' EXIT; run_playbook "$1"' -- - -``` - - - -## `postdeploy/bootiful-postdeploy.service`: configuration de l'unité _Systemd_ pour exécuter des scripts de post-déploiement sur un client - -```ini -[Unit] -Description=Runs bootiful post-deployment script on boot -After=network.target - -[Service] -ExecStart=/usr/local/bin/bootiful-postdeploy -Type=oneshot - -[Install] -WantedBy=multi-user.target -``` - diff --git a/doc/rapport.pdf b/doc/rapport.pdf index 04e093a9fb18e2ae7e793d5f80f2e228c86489ad..6768516db44d99cb3fa9add40f090f2075c60b96 100644 Binary files a/doc/rapport.pdf and b/doc/rapport.pdf differ diff --git a/doc/references.bib b/doc/references.bib index 87842cf037a06f497700dbc7846b503b1421c57a..f606213d03010deaa49c3c4c1f7020f593b3dea9 100644 --- a/doc/references.bib +++ b/doc/references.bib @@ -29,7 +29,7 @@ langid = {english}, } -@online{noauthor_multistrap1_nodate, +@online{multistrap_man, title = {multistrap(1) — multistrap — Debian buster — Debian Manpages}, url = {https://manpages.debian.org/buster/multistrap/multistrap.1.en.html}, urldate = {2020-08-16}