yannstatic/static/2023/05/31/EndeavourOS-Virt-Manager_Complete_Edition.html

2509 lines
217 KiB
HTML
Raw Permalink Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>EndeavourOS Virt-Manager Complete Edition (VMM KVM QEMU) - YannStatic</title>
<meta name="description" content="">
<link rel="canonical" href="https://static.rnmkcy.eu/2023/05/31/EndeavourOS-Virt-Manager_Complete_Edition.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root">
<!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner">
<div class="page__header d-print-none">
<header class="header"><div class="main">
<div class="header__title">
<div class="header__brand">
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewbox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.516 C 137.230 220.679,138.000 221.92
" href="/">YannStatic</a>
</div>
<!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text">
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche -->
</div>
<nav class="navigation">
<ul>
<li class="navigation__item"><a href="/archive.html">Etiquettes</a></li>
<li class="navigation__item"><a href="/htmldoc.html">Documents</a></li>
<li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li>
<li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li>
</ul>
</nav>
</div>
</header>
</div>
<div class="page__content"><div class="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto">
<!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div>
<!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">EndeavourOS Virt-Manager Complete Edition (VMM KVM QEMU)</h1></header></div>
<meta itemprop="headline" content="EndeavourOS Virt-Manager Complete Edition (VMM KVM QEMU)">
<div class="article__info clearfix">
<ul class="left-col menu">
<li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=virtuel">virtuel</a>
2024-10-31 20:18:37 +01:00
</li>
<li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=network">network</a>
2024-10-31 20:18:37 +01:00
</li>
</ul>
<ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF">31 mai   2023</span>
<span title="Modification" style="color:#00FF7F"> 7 oct.  2024</span>
</li></ul>
</div>
<meta itemprop="datePublished" content="2024-10-07T00:00:00+02:00">
<meta itemprop="keywords" content="virtuel,network">
<div class="js-article-content">
<div class="layout--article">
<!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page"></button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody">
<details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><p><img src="/images/vmm-logo.png" alt=""></p>
<p><em>Depuis son introduction, la virtualisation a parcouru un long chemin. De nos jours, les hyperviseurs sont utilisés pour presque tout, de lexécution de serveurs au niveau de lentreprise au test de différents systèmes dexploitation sur un ordinateur utilisateur local.<br>
Nous allons installer lun des meilleurs ensembles doutils gratuits utilisés pour la virtualisation qui se compose de Virt-Manager , KVM et QEMU .(<a href="https://discovery.endeavouros.com/applications/how-to-install-virt-manager-complete-edition/2021/09/">How to install Virt-Manager Complete Edition</a>)</em></p>
<h2 id="virt-manager-complete-edition">Virt-Manager Complete Edition</h2>
<h3 id="installation-simplifiée">Installation simplifiée</h3>
<p>Regroupement des commandes pour une installation rapide</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pacman <span class="nt">-Syu</span> <span class="nt">--needed</span> virt-manager qemu-desktop libvirt edk2-ovmf dnsmasq vde2 bridge-utils iptables-nft dmidecode libguestfs
<span class="c"># activer et lancer le service</span>
<span class="nb">sudo </span>systemctl <span class="nb">enable</span> <span class="nt">--now</span> libvirtd.service
</code></pre></div></div>
<p>Vérifier existance des groupes : <code class="language-plaintext highlighter-rouge">grep -E '^kvm|^libvirt|^qemu|^swtpm' /etc/group</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kvm:x:992:libvirt-qemu,qemu
libvirt:x:961:
libvirt-qemu:x:959:
qemu:x:958:
</code></pre></div></div>
<p>Autoriser Libvirt pour les comptes utilisateurs standards</p>
<p>Puisque nous voulons utiliser notre compte utilisateur Linux standard pour gérer KVM, configurons KVM pour lautoriser.</p>
<p>Ouvrez le fichier /etc/libvirt/libvirtd.conf pour léditer.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nano /etc/libvirt/libvirtd.conf
</code></pre></div></div>
<p>Définissez la propriété du groupe UNIX domain socket à libvirt, (autour de la ligne 85)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unix_sock_group <span class="o">=</span> <span class="s2">"libvirt"</span>
</code></pre></div></div>
<p>Définissez les permissions de socket UNIX pour le socket R/W (autour de la ligne 108).</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unix_sock_rw_perms <span class="o">=</span> <span class="s2">"0770"</span>
</code></pre></div></div>
<p>Ajoutez votre compte utilisateur au groupe libvirt.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>usermod <span class="nt">-a</span> <span class="nt">-G</span> libvirt <span class="si">$(</span><span class="nb">whoami</span><span class="si">)</span>
newgrp libvirt
</code></pre></div></div>
<p>Redémarrez le démon libvirt.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl restart libvirtd.service
</code></pre></div></div>
<p>sudo systemctl restart libvirtd.service</p>
<p>Activer la virtualisation imbriquée (facultatif)</p>
<p>La fonction de virtualisation imbriquée vous permet dexécuter des machines virtuelles à lintérieur dune VM.<br>
Activez la virtualisation imbriquée pour kvm_intel / kvm_amd en activant le module du noyau comme indiqué.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">### Intel Processor ###</span>
<span class="nb">sudo </span>modprobe <span class="nt">-r</span> kvm_intel
<span class="nb">sudo </span>modprobe kvm_intel <span class="nv">nested</span><span class="o">=</span>1
<span class="c">### AMD Processor ###</span>
<span class="nb">sudo </span>modprobe <span class="nt">-r</span> kvm_amd
<span class="nb">sudo </span>modprobe kvm_amd <span class="nv">nested</span><span class="o">=</span>1
</code></pre></div></div>
<p>Pour rendre cette configuration persistante, exécutez :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"options kvm-intel nested=1"</span> | <span class="nb">sudo tee</span> /etc/modprobe.d/kvm-intel.conf
</code></pre></div></div>
<p>Confirmez que la virtualisation imbriquée est réglée sur Oui :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">### Intel Processor ###</span>
<span class="nv">$ </span>systool <span class="nt">-m</span> kvm_intel <span class="nt">-v</span> | <span class="nb">grep </span>nested
nested <span class="o">=</span> <span class="s2">"Y"</span>
nested_early_check <span class="o">=</span> <span class="s2">"N"</span>
<span class="nv">$ </span><span class="nb">cat</span> /sys/module/kvm_intel/parameters/nested
Y
<span class="c">### AMD Processor ###</span>
<span class="nv">$ </span>systool <span class="nt">-m</span> kvm_amd <span class="nt">-v</span> | <span class="nb">grep </span>nested
nested <span class="o">=</span> <span class="s2">"Y"</span>
nested_early_check <span class="o">=</span> <span class="s2">"N"</span>
<span class="nv">$ </span><span class="nb">cat</span> /sys/module/kvm_amd/parameters/nested
Y
</code></pre></div></div>
<h3 id="virt-manager-">Virt-Manager ?</h3>
<p><a href="https://virt-manager.org/">Virt-Manager</a> est un frontal utilisateur graphique pour la bibliothèque qui fournit des services de gestion de machines virtuelles. Linterface Virt-manager permet à lutilisateur de créer, supprimer et manipuler facilement des machines virtuelles sans passer par le terminal.<br>
<img src="/images/vmm001.png" alt=""><br>
<em>Virt-manager prend principalement en charge KVM mais il peut également fonctionner avec dautres hyperviseurs tels que Xen et LXC</em></p>
<p>Lorsque virt-manager est installé, il est livré avec lensemble doutils répertorié ci-dessous.</p>
<ul>
<li>
<strong>virt-install</strong> : utilitaire de ligne de commande pour provisionner le système dexploitation</li>
<li>
<strong>virt-viewer</strong> : Linterface utilisateur avec des fonctionnalités graphiques</li>
<li>
<strong>virt-clone</strong> : outil en ligne de commande pour cloner des hôtes inactifs existants</li>
<li>
<strong>virt-xml</strong> : outil de ligne de commande pour éditer facilement le XML du domaine libvirt à laide des options de ligne de commande de virt-install.</li>
<li>
<strong>virt-bootstrap</strong> : outil de commande fournissant un moyen simple de configurer le système de fichiers racine pour les conteneurs basés sur libvirt.</li>
</ul>
<h3 id="kvm">KVM</h3>
<p>Les lettres <strong><a href="https://www.linux-kvm.org/page/Main_Page">KVM</a></strong> signifient <strong>K</strong> ernel-based <strong>V</strong> irtual <strong>M</strong> achines. KVM est une solution de virtualisation complète Linux pour les processeurs darchitecture x86 qui a lextension de virtualisation (Intel VT et AMD-V).</p>
<p>KVM est un logiciel gratuit et open-source. La prise en charge de KVM est incluse dans tous les nouveaux noyaux Linux par conception.</p>
<h3 id="qemu">QEMU</h3>
<p><strong><a href="https://www.qemu.org/">QEMU</a></strong> est la version abrégée de <strong>Quick EMU</strong>lator, un émulateur open source gratuit capable deffectuer une virtualisation matérielle . Il émule le processeur de la machine hôte via une traduction binaire dynamique. Cela fournit différents ensembles de modèles de matériel et de périphérique pour la machine hôte, ce qui lui permet dexécuter une variété de systèmes invités.</p>
<p>KVM peut être utilisé avec QEMU, ce qui permet aux machines virtuelles dêtre exécutées presque à des vitesses natives. Non seulement lémulation matérielle, QEMU est capable démuler des processeurs de niveau utilisateur qui permettent aux applications compilées pour une architecture de sexécuter sur une autre.</p>
<h3 id="installer-virt-manager-complete-edition">Installer Virt-Manager Complete Edition</h3>
<p>Installation de tous les packages pour exécuter virt-manager.</p>
<p>Installation de base</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -Syu virt-manager qemu-desktop dnsmasq iptables-nft
</code></pre></div></div>
<p>Installation complète</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -Syu --needed virt-manager qemu-desktop libvirt edk2-ovmf dnsmasq vde2 bridge-utils iptables-nft dmidecode swtpm
</code></pre></div></div>
<ul>
<li>
<a href="https://archlinux.org/packages/extra/any/edk2-ovmf/">edk2-ovmf</a> : ovmf est un projet basé sur <a href="https://github.com/tianocore/tianocore.github.io/wiki/EDK-II">EDK</a> II pour activer le support <a href="https://github.com/tianocore/tianocore.github.io/wiki/UEFI">UEFI</a> pour les machines virtuelles.</li>
<li>
<a href="http://edk2-ovmf:%20%20ovmf%20%20is%20an%20EDK%20II%20based%20project%20to%20enable%20UEFI%20support%20for%20Virtual%20Machines.%20iptables-nft%20https://archlinux.org/packages/core/x86_64/iptables-nft/">iptables-nft</a> : outil de contrôle des paquets du noyau Linux (utilisant linterface nft).</li>
<li>
<a href="https://archlinux.org/packages/extra/x86_64/bridge-utils/">bridge-utils</a> : utilitaires de pont Ethernet.</li>
</ul>
<p>Une fois linstallation terminée, vous devez activer le service libvirtd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable --now libvirtd.service
</code></pre></div></div>
<p>Vérifiez létat pour vous assurer que le service est en cours dexécution.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl status libvirtd.service
</code></pre></div></div>
<p>Vous pourrez maintenant commencer à créer votre configuration de machine virtuelle sur lapplication.</p>
<p>La connexion QEMU ne nécessite pas dexécuter libvirtd.service !</p>
<p><strong>Exécutez Virt-Manager en tant quutilisateur normal (recommandé)</strong></p>
<ol>
<li>
<p>Créez des groupes kvm et libvirt sils ne sont pas présents<br>
sudo groupadd -f kvm<br>
sudo groupadd -f libvirt</p>
</li>
<li>
<p>Ajouter lutilisateur actuel aux groupes kvm et libvirt<br>
sudo usermod -aG libvirt $USER<br>
sudo usermod -aG kvm $USER</p>
</li>
<li>
<p>Ajoutez les lignes suivantes à la fin de <code class="language-plaintext highlighter-rouge">/etc/libvirt/libvirtd.conf</code><br>
unix_sock_group = “libvirt”<br>
unix_sock_ro_perms = “0777”<br>
unix_sock_rw_perms = “0770”</p>
</li>
<li>
<p>Ajoutez les lignes suivantes à la fin de /etc/libvirt/qemu.conf<br>
swtpm_user = “swtpm”<br>
swtpm_group = “swtpm”<br>
user = “yann”<br>
group = “yann”</p>
</li>
</ol>
<p>Création dun utilisateur système swtpm</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo useradd -r swtpm
</code></pre></div></div>
<p class="info">Maintenant, redémarrez le système pour vous assurer que les modifications sont appliquées ou au moins déconnectez complètement votre utilisateur en vous déconnectant et en revenant sur le bureau.</p>
<p>Créer et donner les droits au dossier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mkdir -p /var/lib/swtpm-localca
sudo chown swtpm:swtpm /var/lib/swtpm-localca
</code></pre></div></div>
<h4 id="gestionnaire-de-session-utilisateur-inactif">Gestionnaire de session utilisateur (INACTIF)</h4>
<p>Pour terminer la configuration, la dernière étape consiste à remplacer le gestionnaire de session qemu par défaut par le gestionnaire de session utilisateur qemu :<br>
<img src="/images/vmm002.png" alt=""><br>
Ouvrez virt-manager à partir du menu de lapplication. Supprimer la connexion par défaut QEMU/KVM System/Root Session”</p>
<p>Et ajoutez-en une nouvelle à laide de QEMU/KVM User Session :<br>
Fichier → Ajouter une connexion → Hypervieur : QEMU/KVM Session utilisateur → Connexion auto → Enable<br>
<img src="/images/vmm003.png" alt=""> <br>
Vous pouvez maintenant démarrer virt-manager et créer des machines virtuelles en tant quutilisateur non root !</p>
<h4 id="libguestfs-guestfs-tools">libguestfs guestfs-tools</h4>
<p>Si vous souhaitez modifier les images de disque de la machine virtuelle créées, vous pouvez installer libguestfs .
libguestfs est un ensemble doutils utilisés pour accéder aux images disques des machines virtuelles (VM) et les modifier. Vous pouvez lutiliser pour :</p>
<ul>
<li>visualiser et modifier des fichiers à lintérieur des invités</li>
<li>effectuer des changements de script sur les VMs</li>
<li>surveiller les statistiques sur les disques utilisés/libres</li>
<li>créer des invités</li>
<li>P2V</li>
<li>V2V</li>
<li>effectuer des sauvegardes, etc.</li>
</ul>
<p>Installation</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S libguestfs guestfs-tools
</code></pre></div></div>
<h4 id="fonctionnalité-facultative">Fonctionnalité facultative</h4>
<p>Forfait pour des fonctionnalités supplémentaires :</p>
<ul>
<li>
<a href="https://www.archlinux.org/packages/?name=qemu-arch-extra">qemu-arch-extra</a> - prise en charge darchitectures supplémentaires</li>
<li>
<a href="https://www.archlinux.org/packages/?name=qemu-block-gluster">qemu-block-gluster</a> Prise en charge des blocs <a href="https://wiki.archlinux.org/index.php/Glusterfs">Glusterfs</a>
</li>
<li>
<a href="https://www.archlinux.org/packages/?name=qemu-block-iscsi">qemu-block-iscsi</a> - Prise en charge des blocs <a href="https://wiki.archlinux.org/index.php/ISCSI">iSCSI</a>
</li>
<li>
<a href="https://www.archlinux.org/packages/?name=qemu-block-rbd">qemu-block-rbd</a> - Prise en charge du bloc RBD</li>
</ul>
<p><strong>qemu-emulators-full</strong><br>
Tous les émulateurs de mode utilisateur et de système QEMU</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S qemu-emulators-full
</code></pre></div></div>
<p><strong>qemu-bloc-gluster</strong><br>
Glusterfs est un système de fichiers réseau évolutif. Cela ajoute la prise en charge du bloc Glusterfs à QEMU.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S qemu-block-gluster
</code></pre></div></div>
<p><strong>qemu-block-iscsi</strong><br>
iSCI permet laccès au stockage via un réseau. qemu-block-iscsi permet à QEMU de bloquer cela.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S qemu-block-iscsi
</code></pre></div></div>
<p><strong>samba</strong><br>
Cela ajouterait la prise en charge de SMB/CIFS à QEMU.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yay -S samba
</code></pre></div></div>
<h4 id="résumé---installation-complète-avec-outis">Résumé - Installation complète avec outis</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -Syu --needed virt-manager qemu-desktop libvirt edk2-ovmf dnsmasq vde2 bridge-utils iptables-nft dmidecode swtpm libguestfs guestfs-tools
</code></pre></div></div>
<h3 id="activation-des-graphiques-virtio-avec-accélération-3d">Activation des graphiques virtio avec accélération 3D</h3>
<p>Il existe plusieurs modes daccélérations graphiques pris en charge par virt-manager.</p>
<ul>
<li>QXL - Carte graphique paravirtuelle QXL. Il est compatible VGA (y compris le support VESA 2.0 VBE). Fonctionne mieux avec les pilotes invités QXL installés. Choix recommandé lors de lutilisation du protocole dépices.</li>
<li>Bochs - Le BIOS VGA Bochs prend en charge, dans une certaine mesure, la spécification VBE.</li>
<li>Ramfb - Un tampon de trame simple qui vit dans la RAM invitée et est configuré via fw_cfg.</li>
<li>VGA Carte VGA standard</li>
<li>Virtio Virtio-gpu est un pilote graphique accéléré 3D paravirtualisé.</li>
</ul>
<p>Lors de lutilisation de virtio avec laccélération 3D, virt-manager ne fonctionne parfois pas et génère une erreur. Ce nest en fait pas virt-manager, cest qemu qui plante.<br>
<img src="/images/vmm004.png" alt=""> <br>
Erreur QEMU lorsque laccélération 3D est activée</p>
<p>Pour utiliser virtio avec laccélération 3D, vous devez configurer virt-manager comme indiqué ci-dessous.</p>
<p class="warning">REMARQUE : Au moment de la rédaction, il existe un problème lors de lutilisation de virtio avec laccélération 3D avec XFCE DE, ce qui entraîne un écran noir pour les invités. Cela est dû au compositeur xfwm4+compositing par défaut. La solution de contournement connue consiste à utiliser QXL ou virtio sans accélération 3D.</p>
<p>Accédez à Display Spice et cochez la case Open GL et sélectionnez Auto dans le menu déroulant.<br>
<img src="/images/vmm005.png" alt=""> <br>
Avant de configurer</p>
<p><img src="/images/vmm006.png" alt=""> <br>
Après avoir configuré</p>
<p>Allez maintenant dans Vidéo et sélectionnez virtio puis cochez la case Accélération 3D.<br>
<img src="/images/vmm007.png" alt=""> <br>
Après avoir configuré les options vidéo</p>
<p>Après une telle configuration, vous pouvez profiter dune VM invitée entièrement accélérée en 3D. Pensez également à cliquer sur Appliquer .</p>
<p>Installation des pilotes invités virtio pour Windows</p>
<p>RedHat fournit un ensemble de pilotes invités pour virtio qui couvre les pilotes graphiques du système invité. Vous pouvez télécharger les derniers pilotes depuis leur page GitHub virtio-win-pkg-scripts <a href="https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md">ici</a> .</p>
<h3 id="modifier-emplacement-pool-stockage">Modifier emplacement pool stockage</h3>
<p><em>Modifier lemplacement du pool de stockage par défaut de KVM Libvirt à laide de Virt-manager</em></p>
<p>Virt-manager, abréviation de Virtual Machine Manager, est une interface graphique utilisée pour gérer les machines virtuelles via libvirt.</p>
<p>Ouvrir lapplication Virt-manager. Faire un clic droit sur <strong>QEMU/KVM</strong> et cliquer sur loption <strong>Détails</strong>.<br>
<img src="/images/vmm010.png" alt=""><br>
Voir les détails de la connexion KVMVoir les détails de la connexion KVM<br>
Vous pouvez aussi cliquer sur Edition → Details de la connexion à partir de linterface Virt-manager.</p>
<p>Sous la section Stockage, vous verrez lemplacement du pool de stockage par défaut.<br>
<img src="/images/vmm011.png" alt=""><br>
Emplacement du pool de stockage par défaut de KVM Libvirt</p>
<p>Arrêter et supprimer le pool de stockage par défaut KVM Libvirt<br>
<img src="/images/vmm012.png" alt=""><br>
Ceci désactivera et supprimera le pool par défaut.</p>
<p>Cliquez sur le signe plus (+) dans le volet inférieur gauche pour créer un nouveau pool de stockage à utiliser par les machines virtuelles.</p>
<p>Saisissez le nom du pool de stockage (par exemple, default dans mon cas).<br>
<img src="/images/vmm013.png" alt=""><br>
Créer un nouveau pool de stockage KVM Libvirt</p>
<p>Le nouveau stockage est maintenant actif. Cochez la case Démarrage automatique pour démarrer automatiquement le nouveau pool de stockage au démarrage du système.<br>
<img src="/images/vmm014.png" alt=""><br>
Nouvel emplacement du pool de stockage KVM Libvirt</p>
<blockquote>
<p>Si nécessaire, déplacez toutes les images VM de lancien répertoire de stockage vers le nouveau.</p>
</blockquote>
<p>Redémarrez le service libvirtd</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl restart libvirtd
</code></pre></div></div>
<p>Vérification</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-info default
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Nom : default
UUID : 23c3aee6-57bb-4c98-93f8-4c15ab3c9fae
État : en cours dexécution
Persistant : oui
Démarrage automatique : oui
Capacité : 58,76 GiB
Allocation : 17,95 GiB
Disponible : 40,81 GiB
</code></pre></div></div>
<h2 id="réseau">Réseau</h2>
<p>La connexion des machines virtuelles (VM) à dautres périphériques et emplacements sur un réseau doit être facilitée par le matériel hôte. Les sections suivantes expliquent les mécanismes des connexions réseau des VM et décrivent le paramètre réseau VM par défaut.</p>
<h3 id="fonctionnement-des-réseaux-virtuels">Fonctionnement des réseaux virtuels</h3>
<p>Le réseau virtuel utilise le concept de commutateur de réseau virtuel. Un commutateur de réseau virtuel est une construction logicielle qui fonctionne sur une machine hôte.<br>
Les machines virtuelles se connectent au réseau par lintermédiaire du commutateur de réseau virtuel. En fonction de la configuration du commutateur virtuel, une machine virtuelle peut utiliser un réseau virtuel existant géré par lhyperviseur ou une méthode de connexion réseau différente.</p>
<p>La figure suivante montre un commutateur de réseau virtuel qui connecte deux machines virtuelles au réseau :<br>
<img src="/images/virtual-network01.png" alt=""></p>
<p>Du point de vue dun système dexploitation invité, une connexion réseau virtuelle est identique à une connexion réseau physique.<br>
Les machines hôtes considèrent les commutateurs réseau virtuels comme des interfaces réseau.<br>
Lorsque le service <strong>libvirtd</strong> est installé et démarré pour la première fois, il crée <strong>virbr0</strong>, linterface réseau par défaut des machines virtuelles.</p>
<p>Pour afficher des informations sur cette interface, utilisez lutilitaire ip sur lhôte.</p>
<p class="info">Par défaut, toutes les machines virtuelles dun même hôte sont connectées au même réseau virtuel de type NAT, appelé <strong>default</strong>, qui utilise linterface <strong>virbr0</strong></p>
<p>Si vous avez besoin dune fonctionnalité réseau différente, vous pouvez créer des réseaux virtuels et des interfaces réseau supplémentaires et configurer vos machines virtuelles pour quelles les utilisent. En plus du NAT par défaut, ces réseaux et interfaces peuvent être configurés pour utiliser lun des modes suivants :</p>
<h4 id="mise-en-réseau-virtuelle-en-mode-routé">Mise en réseau virtuelle en mode routé</h4>
<p>En mode routé, le commutateur virtuel se connecte au réseau local physique connecté à la machine hôte et transmet le trafic dans les deux sens sans utiliser de NAT. Le commutateur virtuel peut examiner lensemble du trafic et utiliser les informations contenues dans les paquets réseau pour prendre des décisions de routage.<br>
Dans ce mode, les machines virtuelles (VM) se trouvent toutes dans un sous-réseau unique, séparé de la machine hôte.<br>
Le sous-réseau des machines virtuelles est acheminé via un commutateur virtuel, qui existe sur la machine hôte. Cela permet détablir des connexions entrantes, mais nécessite des entrées supplémentaires dans la table de routage pour les systèmes sur le réseau externe.</p>
<p>Le mode routé utilise le routage basé sur ladresse IP :<br>
<img src="/images/virtual-network02.png" alt=""></p>
<p>Lhébergement de serveurs virtuels (VSH) est une topologie courante qui utilise le mode routé. Un fournisseur VSH peut avoir plusieurs machines hôtes, chacune avec deux connexions réseau physiques. Une interface est utilisée pour la gestion et la comptabilité, lautre pour la connexion des machines virtuelles. Chaque VM possède sa propre adresse IP publique, mais les machines hôtes utilisent des adresses IP privées afin que seuls les administrateurs internes puissent gérer les VM.</p>
<h4 id="mise-en-réseau-virtuelle-en-mode-ponté">Mise en réseau virtuelle en mode ponté</h4>
<p>Dans la plupart des modes de mise en réseau de VM, les VM créent automatiquement le pont virtuel virbr0 et sy connectent.<br>
En revanche, en <strong>mode ponté</strong>, la VM se connecte à un pont Linux existant sur lhôte.<br>
Par conséquent, la VM est directement visible sur le réseau physique.<br>
Cela permet les connexions entrantes, mais ne nécessite pas dentrées supplémentaires dans la table de routage.</p>
<p>Le mode ponté utilise la commutation de connexion basée sur ladresse MAC :<br>
<img src="/images/virtual-network03.png" alt=""></p>
<p>En mode ponté, la VM apparaît dans le même sous-réseau que la machine hôte.<br>
Toutes les autres machines physiques sur le même réseau physique peuvent détecter la VM et y accéder.</p>
<p><strong>Liaison entre réseaux pontés</strong></p>
<p>Il est possible dutiliser plusieurs interfaces de pont physique sur lhyperviseur en les reliant par un lien. Le lien peut ensuite être ajouté à un pont, après quoi les machines virtuelles peuvent également être ajoutées au pont. Cependant, le pilote de liaison a plusieurs modes de fonctionnement, et tous ces modes ne fonctionnent pas avec une passerelle où des machines virtuelles sont en cours dutilisation.</p>
<p>Les modes de liaison (<a href="https://access.redhat.com/solutions/67546">bonding modes</a>) suivants sont utilisables :</p>
<ul>
<li>mode 1</li>
<li>mode 2</li>
<li>mode 4</li>
</ul>
<p>En revanche, lutilisation des modes 0, 3, 5 ou 6 risque de faire échouer la connexion. Notez également que la surveillance de linterface indépendante du média (MII) doit être utilisée pour surveiller les modes de liaison, car la surveillance du protocole de résolution dadresses (ARP) ne fonctionne pas correctement.</p>
<p>Pour plus dinformations sur les modes de liaison, reportez-vous à la base de connaissances de Red Hat ( <a href="https://access.redhat.com/solutions/67546">Red Hat Knowledgebase</a>)</p>
<p><strong>Scénarios courants</strong></p>
<p>Les cas dutilisation les plus courants du mode ponté sont les suivants :</p>
<ul>
<li>Déployer des machines virtuelles dans un réseau existant aux côtés de machines hôtes, en rendant la différence entre les machines virtuelles et physiques invisible pour lutilisateur final.</li>
<li>Déploiement de machines virtuelles sans modification des paramètres de configuration du réseau physique existant.</li>
<li>Déployer des machines virtuelles qui doivent être facilement accessibles à un réseau physique existant. Placer des machines virtuelles sur un réseau physique où elles doivent accéder aux services DHCP.</li>
<li>Connecter des machines virtuelles à un réseau existant où des réseaux locaux virtuels (VLAN) sont utilisés.</li>
<li>Un réseau de zone démilitarisée (DMZ). Pour un déploiement DMZ avec des machines virtuelles, Red Hat recommande de configurer la DMZ au niveau du routeur et des commutateurs du réseau physique, et de connecter les machines virtuelles au réseau physique en utilisant le mode ponté.</li>
</ul>
<h4 id="mise-en-réseau-virtuelle-en-mode-isolé">Mise en réseau virtuelle en mode isolé</h4>
<p>En mode isolé, les machines virtuelles connectées au commutateur virtuel peuvent communiquer entre elles et avec la machine hôte, mais leur trafic ne passe pas en dehors de la machine hôte et elles ne peuvent pas recevoir de trafic en provenance de lextérieur de la machine hôte. Lutilisation de dnsmasq dans ce mode est nécessaire pour les fonctionnalités de base telles que DHCP.<br>
<img src="/images/virtual-network04.png" alt=""></p>
<h4 id="réseau-virtuel-en-mode-ouvert">Réseau virtuel en mode ouvert</h4>
<p>Lors de lutilisation du mode ouvert pour la mise en réseau, libvirt ne génère aucune règle de pare-feu pour le réseau. Par conséquent, libvirt nécrase pas les règles de pare-feu fournies par lhôte, et lutilisateur peut donc gérer manuellement les règles de pare-feu de la VM.</p>
<h4 id="comparaison-des-types-de-connexion-des-machines-virtuelles">Comparaison des types de connexion des machines virtuelles</h4>
<p>Le tableau suivant fournit des informations sur les emplacements auxquels certains types de configurations réseau de machines virtuelles (VM) peuvent se connecter et sur lesquels elles sont visibles.</p>
<p>Types de connexion de machine virtuelle</p>
<table>
<thead>
<tr>
<th><strong>__</strong></th>
<th>Connexion à lhôte</th>
<th>Connexion à dautres VM sur lhôte</th>
<th>Connexion aux sites extérieurs</th>
<th>Visible aux sites extérieurs</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mode ponté</td>
<td>Oui</td>
<td>Oui</td>
<td>Oui</td>
<td>Oui</td>
</tr>
<tr>
<td>NAT</td>
<td>Oui</td>
<td>Oui</td>
<td>Oui</td>
<td>Non</td>
</tr>
<tr>
<td>Mode routé</td>
<td>Oui</td>
<td>Oui</td>
<td>Oui</td>
<td>Oui</td>
</tr>
<tr>
<td>Mode isolé</td>
<td>Oui</td>
<td>Oui</td>
<td>Non</td>
<td>Non</td>
</tr>
</tbody>
</table>
<p>Mode ouvert : Dépend des règles du pare-feu de lhôte</p>
<h3 id="hôte---networkmanager-bridge-network-br0-sur-interface-par-défaut">Hôte - NetworkManager Bridge Network br0 sur interface par défaut</h3>
<p>En mode su</p>
<p>Les connexions actives</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli connection show --active
</code></pre></div></div>
<p><img src="/images/nmcli-100.png" alt="nmcli"></p>
<p>Jai une «Ethernet automatique» qui utilise linterface Ethernet <strong>enp0s31f6</strong>.<br>
Je vais configurer une interface de pont nommée br0 et ajouter (ou asservir) linterface <strong>enp0s31f6</strong>.</p>
<h4 id="créer-un-pont-br0">Créer un Pont br0</h4>
<p><strong>Créer un pont nommé br0</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con add ifname br0 <span class="nb">type </span>bridge con-name br0
<span class="nb">sudo </span>nmcli con modify br0 bridge.stp no
<span class="c"># désactiver IPV6 sur br0</span>
<span class="nb">sudo </span>nmcli connection modify br0 ipv6.method <span class="s2">"disabled"</span>
</code></pre></div></div>
<p>Affichez les interfaces réseau et notez le(s) nom(s) des interfaces que vous souhaitez ajouter au pont</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli device status
</code></pre></div></div>
<p><img src="/images/nmcli-101.png" alt="nmcli"></p>
<p>Affectez les interfaces à la passerelle.<br>
Si les interfaces que vous souhaitez affecter à la passerelle ne sont pas configurées, créez de nouveaux profils de connexion pour elles</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con add <span class="nb">type </span>bridge-slave ifname enp0s31f6 master br0
</code></pre></div></div>
<p>Définir la connexion existante, celle utilise enp0s31f6, comme “down” (vous pouvez lobtenir avec nmcli connection show active) :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con down e7d9d5a6-8d41-3015-9c5a-3ea38d3d8c78
</code></pre></div></div>
<p>Mettre en place le nouveau pont :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con up br0
</code></pre></div></div>
<h4 id="box---bail-statique">Box - bail statique</h4>
<p><strong>Box - bail statique</strong><br>
Paramétrer un bail statique sur la box avec ladresse mac de la connexion br0<br>
<img src="/images/nmcli-105.png" alt="nmcli"></p>
<p>Utilisez lutilitaire ip pour afficher létat des liens des périphériques Ethernet qui sont des ports dun pont spécifique :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a show br0
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>5: br0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 5e:95:2e:b1:9f:c4 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.20/24 brd 192.168.0.255 scope global dynamic noprefixroute br0
valid_lft 42855sec preferred_lft 42855sec
</code></pre></div></div>
<h4 id="pont-réseau-virtuel-host-bridge">Pont réseau virtuel “host-bridge”</h4>
<p><strong>Les commandes <code class="language-plaintext highlighter-rouge">virsh</code> en mode su</strong></p>
<p>Déclarer le pont (bridge) à KVM.<br>
Créer un fichier de définition de réseau au format XML :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano host-bridge.xml
</code></pre></div></div>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;network&gt;</span>
<span class="nt">&lt;name&gt;</span>host-bridge<span class="nt">&lt;/name&gt;</span>
<span class="nt">&lt;forward</span> <span class="na">mode=</span><span class="s">"bridge"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;bridge</span> <span class="na">name=</span><span class="s">"br0"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/network&gt;</span>
</code></pre></div></div>
<p>Appliquer la configuration :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>virsh net-define host-bridge.xml <span class="c"># -&gt; Réseau host-bridge défini depuis host-bridge.xml</span>
<span class="nb">sudo </span>virsh net-start host-bridge <span class="c"># -&gt; Réseau host-bridge démarré</span>
<span class="nb">sudo </span>virsh net-autostart host-bridge <span class="c"># -&gt; Réseau host-bridge marqué en démarrage automatique</span>
</code></pre></div></div>
<p>Vérification</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh net-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique Persistant
-------------------------------------------------------------
host-bridge actif oui oui
</code></pre></div></div>
<p><img src="/images/virtual-network05.png" alt=""></p>
<h3 id="hôte---networkmanager-bridge-network-br1-sur-autre-interface">Hôte - NetworkManager Bridge Network br1 sur autre interface</h3>
<p>On dispose dune seconde ou plus interface réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2: enp0s31f6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether 38:d5:47:7c:a0:6c brd ff:ff:ff:ff:ff:ff
3: enp3s0f0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq master br0 state UP mode DEFAULT group default qlen 1000
link/ether 6c:b3:11:32:04:c8 brd ff:ff:ff:ff:ff:ff
5: enp3s0f1: &lt;NO-CARRIER,BROADCAST,MULTICAST,UP&gt; mtu 1500 qdisc mq state DOWN mode DEFAULT group default qlen 1000
link/ether 6c:b3:11:32:04:c9 brd ff:ff:ff:ff:ff:ff
</code></pre></div></div>
<p>Dans ce cas, 3 interfaces réseau, enp0s31f6 est linterface affectée à br0 et utilisée par lhôte</p>
<h4 id="créer-un-pont-br1">Créer un Pont br1</h4>
<p><strong>Créer un pont nommé br1</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con add ifname br1 <span class="nb">type </span>bridge con-name br1
<span class="nb">sudo </span>nmcli con modify br1 bridge.stp no
<span class="c"># désactiver IPV6 sur br1</span>
<span class="nb">sudo </span>nmcli connection modify br1 ipv6.method <span class="s2">"disabled"</span>
</code></pre></div></div>
<p>Affichez les interfaces réseau et notez le(s) nom(s) des interfaces que vous souhaitez ajouter au pont</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli device status
</code></pre></div></div>
<p><img src="/images/nmcli-101a.png" alt="nmcli"></p>
<p>Affectez les interfaces à la passerelle.<br>
Si les interfaces que vous souhaitez affecter à la passerelle ne sont pas configurées, créez de nouveaux profils de connexion pour elles</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con add <span class="nb">type </span>bridge-slave ifname enp3s0f0 master br1
</code></pre></div></div>
<p>Définir la connexion existante, celle qui utilise enp3s0f0, comme “down” (vous pouvez lobtenir avec nmcli connection show active) :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con down 15950896-b8dc-39c8-9f21-f20f13086975
</code></pre></div></div>
<p>Mettre en place le nouveau pont :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli con up br1
</code></pre></div></div>
<p>Utilisez lutilitaire ip pour afficher létat des liens des périphériques Ethernet qui sont des ports dun pont spécifique :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a show br1
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>9: br1: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 26:89:96:2c:1d:9d brd ff:ff:ff:ff:ff:ff
inet 192.168.10.180/24 brd 192.168.10.255 scope global dynamic noprefixroute br1
valid_lft 85978sec preferred_lft 85978sec
</code></pre></div></div>
<p>Au final<br>
<img src="/images/nmcli-101b.png" alt="nmcli"></p>
<h4 id="pont-réseau-virtuel-host-tenda">Pont réseau virtuel “host-tenda”</h4>
<p><strong>Les commandes <code class="language-plaintext highlighter-rouge">virsh</code> en mode su</strong></p>
<p>Déclarer le pont (bridge) à KVM.<br>
Créer un fichier de définition de réseau au format XML :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano host-tenda.xml
</code></pre></div></div>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;network&gt;</span>
<span class="nt">&lt;name&gt;</span>host-tenda<span class="nt">&lt;/name&gt;</span>
<span class="nt">&lt;forward</span> <span class="na">mode=</span><span class="s">"bridge"</span><span class="nt">/&gt;</span>
<span class="nt">&lt;bridge</span> <span class="na">name=</span><span class="s">"br1"</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/network&gt;</span>
</code></pre></div></div>
<p>Appliquer la configuration :</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>virsh net-define host-tenda.xml <span class="c"># -&gt; Réseau host-tenda défini depuis host-tenda.xml</span>
<span class="nb">sudo </span>virsh net-start host-tenda <span class="c"># -&gt; Réseau host-tenda démarré</span>
<span class="nb">sudo </span>virsh net-autostart host-tenda <span class="c"># -&gt; Réseau host-tenda marqué en démarrage automatique</span>
</code></pre></div></div>
<p>Vérification</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh net-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique Persistant
-------------------------------------------------------------
host-tenda actif oui oui
</code></pre></div></div>
<p><img src="/images/virtual-network05a.png" alt=""></p>
</div>
<div class="d-print-none">
<footer class="article__footer"><meta itemprop="dateModified" content="2023-05-31T00:00:00+02:00">
<!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix">
<div class="previous">
<span>PRÉCÉDENT</span><a href="/2023/05/29/PC1-EndeavourOS-XFCE-ASUS_H110M_A_conteneur_nspawn_debian_bullseye_nspyan.html">PC1 Ordinateur Bureau EndeavourOS xfce --&gt; conteneur nspawn debian bullseye nspyan</a>
</div>
<div class="next">
<span>SUIVANT</span><a href="/2023/06/02/Qemu-KVM-Machine_virtuelle_debian_11_image_cloud_Qcow2.html">PC1 Qemu/KVM - Machine virtuelle vm-bullseyes debian 11 (image cloud Qcow2)</a>
</div>
</div>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div>
<section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal">
<script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text">
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>