yannstatic/static/2022/10/25/Archlinux-KVM_QEMU-VMM.html

3352 lines
268 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>Archlinux Installer KVM QEMU + VMM + Pont réseau - YannStatic</title>
<meta name="description" content="Archlinux - KVM/QEMU + VMM">
<link rel="canonical" href="https://static.rnmkcy.eu/2022/10/25/Archlinux-KVM_QEMU-VMM.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.5
" 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;">Archlinux Installer KVM QEMU + VMM + Pont réseau</h1></header></div><meta itemprop="headline" content="Archlinux Installer KVM QEMU + VMM + Pont réseau"><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>&nbsp;<span title="Création" style="color:#FF00FF">25&nbsp;oct.&nbsp;&nbsp;2022</span>
<span title="Modification" style="color:#00FF7F">1r&nbsp;mars&nbsp;&nbsp;2023</span></li></ul></div><meta itemprop="datePublished" content="2023-03-01T00:00:00+01: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">&#8679;</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><h2 id="archlinux---kvmqemu--vmm">Archlinux - KVM/QEMU + VMM</h2>
<p><img src="/images/kvm-logo.png" alt="KVM" /> <img src="/images/Qemu_logo_blanc.png" alt="Qemu" /> <img src="/images/kvm-virt.png" alt="KVM" /></p>
<h3 id="description">Description</h3>
<p><strong>KVM</strong> est une bifurcation de <strong>QEMU</strong>. Le code KVM est modifié pour prendre en charge laccélération matérielle lorsquelle est disponible (même architecture pour la VM hôte et la VM invitée).</p>
<p>La plupart du temps, la <strong>QEMU</strong> est utilisée pour émuler une autre architecture (par exemple, émuler ARM/Power arch. en utilisant un processeur x86. Exemple : faire tourner une image RaspberryPI qui fonctionne sur ARM dans un ordinateur équipé dun processeur Intel)</p>
<p>Une différence entre les deux est que la <strong>QEMU</strong> fonctionne sur un processeur sans avoir besoin dune extension de virtualisation matérielle (Intel VT/VT-d, AMD-V) alors que la <strong>KVM</strong> lutilise. Les extensions de virtualisation matérielle vous permettent daccéder directement au matériel sur la machine physique. Linconvénient est que la base de code <strong>KVM</strong> ne peut pas émuler une autre architecture.</p>
<p><u>Simuler une machine complète pour avoir son propre environnement dexécution.</u><br />
<em>Les avantages sont nombreux, isolations des processus, plusieurs environnements différents, etc…</em></p>
<p>La virtualisation matérielle est possible au moyen de ce que lon appelle des hyperviseurs.<br />
<strong>Il existe plusieurs types dhyperviseurs, classés en 2 niveaux.</strong></p>
<ul>
<li>Le niveau 1 est dit “natif”.
<ul>
<li><strong>Natif</strong> car les instructions processeurs du système virtuelle sont directement transmis aux hardware. Il faut donc vérifier la compatibilité entre les systèmes virtualisés et les composants matérielles.</li>
</ul>
</li>
<li>Le niveau 2 est dit “hosted”.
<ul>
<li><strong>Hosted</strong> car la virtualisation seffectue grâce à un logiciel installé sur un système dexploitation. Donc la machine virtualisée ninteragit pas directement avec le Hardware.</li>
</ul>
</li>
</ul>
<p><strong><u>KVM est un hyperviseur de type 1</u></strong>, il est intégré de manière native à beaucoup de distribution basées sur le noyau Linux. KVM pour Kernel-based Virtual Machine car il transforme le noyau linux sur lequel il est exécuté en hyperviseur, proxmox est basé dessus.
Il en existe dautres.</p>
<p>On utilise <strong>QEMU</strong> (QuickEmulator) pour interagir avec <strong>KVM</strong>.</p>
<ul>
<li><u>QEMU est de type 1 et 2</u>.
<ul>
<li>Il peut simuler un environnement pour une machine totalement différente de la votre, par exemple une py sur un PC. Dans ce cas la il transforme les exécutions de processeurs pour les rendre compatibles avec le hardware, donc la il est de <strong>type 2</strong>.</li>
<li>Mais quand il est utilise avec <strong>KVM</strong> dans ce cas la il fonctionne en <strong>type 1</strong> avec des performances bien meilleures.</li>
</ul>
</li>
</ul>
<blockquote>
<p>En clair <strong>Qemu sert à manager les machines virtuels, cest un client</strong>.<br />
Et la liaison entre Qemu et KVM est faite via lAPI libvirt ( management du réseau, stockages, clavier, souris, etc )</p>
</blockquote>
<p><a href="http://libvirt.org/">libvirt</a> est une bibliothèque permettant dinteragir avec différentes solutions de virtualisation (cet article sintéressera uniquement à KVM/QEMU, mais Xen, VirtualBox et dautres sont aussi possibles)<br />
<a href="http://virt-manager.org/">Virtual Machine Manager</a> est un ensemble dapplications permettant de gérer les machines virtuelles</p>
<p>En mode graphique :</p>
<ul>
<li><strong>virt-viewer</strong> est une interface graphique permettant de se connecter sur une machine virtuelle</li>
<li><strong>virt-manager</strong> est une interface graphique permettant de gérer les machines virtuelles</li>
</ul>
<p>En ligne de commande :</p>
<ul>
<li><strong>virt-clone</strong> permet de dupliquer une machine existante</li>
<li><strong>virt-convert</strong> permet de convertir limage dune machine</li>
<li><strong>virt-image</strong> permet de créer un nouvelle machine à partir dune image</li>
<li><strong>virt-install</strong> permet de créer une nouvelle machine ou dimporter une machine déjà créé ultérieurement avec qemu ou qemu-kvm</li>
</ul>
<p><a href="http://www.nongnu.org/qemu/">QEMU</a> est une <u>solution d'émulation et de virtualisation</u> (avec kqemu un pilote permettant doptimiser lémulation lorsque quelle concerne la même architecture).QEMU peut utiliser KVM lors de lexécution dune architecture cible identique à larchitecture hôte. Par exemple, lorsque vous exécutez qemu-system-x86 sur un processeur compatible x86, vous pouvez profiter de laccélération KVM - ce qui vous donne un avantage pour votre hôte et votre système invité.</p>
<p><a href="http://www.linux-kvm.org/page/Main_Page">KVM</a> (<em>Kernel-based Virtual Machine</em>) est une <u>solution de virtualisation</u>, pour les processeurs disposant des capacités nécessaires, et intégré au noyau linux.Il supporte les processeurs Intel et AMD récents (x86 et x86_64), PPC 440, PPC 970, S/390, ARM (Cortex A15, AArch64), et les processeurs MIPS32.</p>
<font color="red"><b>Vous ne pouvez pas utiliser KVM en même temps que VirtualBox. Il faudra en effet fermer KVM pour utiliser VirtualBox et vice versa. Ou désactiver le support de la virtualisation processeur dans VirtualBox</b></font>
<p class="warning">Contrairement à dautres programmes de virtualisation tels que VirtualBox et VMware, QEMU ne fournit pas dinterface graphique pour gérer les machines virtuelles (autre que la fenêtre qui apparaît lors de lexécution dune machine virtuelle), ni un moyen de créer des machines virtuelles persistantes avec des paramètres sauvegardés. Tous les paramètres pour exécuter une machine virtuelle doivent être spécifiés sur la ligne de commande à chaque lancement, sauf si vous avez créé un script personnalisé pour démarrer votre (vos) machine(s) virtuelle(s).</p>
<h3 id="prérequis">Prérequis</h3>
<p><strong>Support matériel</strong></p>
<p>KVM exige que le processeur de lhôte de la machine virtuelle soit compatible avec la virtualisation (nommé VT-x pour les processeurs Intel et AMD-V pour les processeurs AMD). Vous pouvez vérifier si votre processeur prend en charge la virtualisation matérielle à laide de la commande suivante :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>LC_ALL=C lscpu | grep Virtualization
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Virtualization: VT-x
</code></pre></div></div>
<p>Si rien nest affiché après lexécution de la commande, alors votre processeur ne prend pas en charge la virtualisation matérielle et vous ne pourrez pas utiliser KVM.</p>
<blockquote>
<p><strong>Remarque</strong> : Vérifier lactivation de la prise en charge de la virtualisation dans le BIOS.</p>
</blockquote>
<p><strong>Support du noyau</strong></p>
<p>Les noyaux Arch Linux fournissent les modules de noyau appropriés pour supporter KVM et VIRTIO.</p>
<p><strong>Modules KVM</strong></p>
<p>Vous pouvez vérifier si les modules nécessaires (kvm et lun de kvm_amd, kvm_intel) sont disponibles dans votre noyau avec la commande suivante (en supposant que votre noyau est compilé avec CONFIG_IKCONFIG_PROC) :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zgrep CONFIG_KVM /proc/config.gz
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CONFIG_KVM_GUEST=y
CONFIG_KVM_MMIO=y
CONFIG_KVM_ASYNC_PF=y
CONFIG_KVM_VFIO=y
CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y
CONFIG_KVM_COMPAT=y
CONFIG_KVM_XFER_TO_GUEST_WORK=y
CONFIG_KVM=m
CONFIG_KVM_WERROR=y
CONFIG_KVM_INTEL=m
CONFIG_KVM_AMD=m
CONFIG_KVM_AMD_SEV=y
CONFIG_KVM_MMU_AUDIT=y
</code></pre></div></div>
<blockquote>
<p>Le module nest disponible que sil est réglé sur y ou m.</p>
</blockquote>
<p><strong>Dispositifs para-virtualisés</strong></p>
<p><em>La para-virtualisation fournit un moyen de communication rapide et efficace permettant aux invités dutiliser des appareils sur la machine hôte. KVM fournit des périphériques para-virtualisés aux machines virtuelles en utilisant lAPI Virtio comme couche entre lhyperviseur et linvité.</em></p>
<p>Tous les périphériques virtio ont deux parties : le périphérique hôte et le pilote invité.</p>
<p>Utilisez la commande suivante pour vérifier si les modules nécessaires sont disponibles :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zgrep VIRTIO /proc/config.gz
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CONFIG_BLK_MQ_VIRTIO=y
CONFIG_VIRTIO_VSOCKETS=m
CONFIG_VIRTIO_VSOCKETS_COMMON=m
CONFIG_NET_9P_VIRTIO=m
CONFIG_VIRTIO_BLK=m
CONFIG_SCSI_VIRTIO=m
CONFIG_VIRTIO_NET=m
CONFIG_CAIF_VIRTIO=m
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_DRM_VIRTIO_GPU=m
CONFIG_VIRTIO=y
CONFIG_VIRTIO_MENU=y
CONFIG_VIRTIO_PCI=m
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_VDPA=m
CONFIG_VIRTIO_PMEM=m
CONFIG_VIRTIO_BALLOON=m
CONFIG_VIRTIO_MEM=m
CONFIG_VIRTIO_INPUT=m
CONFIG_VIRTIO_MMIO=m
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
CONFIG_RPMSG_VIRTIO=m
CONFIG_VIRTIO_FS=m
CONFIG_CRYPTO_DEV_VIRTIO=m
</code></pre></div></div>
<p><strong>Chargement des modules du noyau</strong></p>
<p>Les modules kvm et kvm-intel/kvm-amd doivent être chargés automatiquement, sinon, voir la page traitant des <a href="https://wiki.archlinux.fr/Kernel_modules">modules du noyau</a>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lsmod | grep kvm
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kvm_intel 323584 0
kvm 851968 1 kvm_intel
irqbypass 16384 1 kvm
</code></pre></div></div>
<blockquote>
<p><strong>Astuce</strong> : Si modprobing kvm_intel ou kvm_amd échoue mais que modprobing kvm réussit, (et lscpu prétend que laccélération matérielle est supportée), vérifiez vos paramètres BIOS.</p>
</blockquote>
<h3 id="installer-virt-manager-kvm-et-qemu">Installer Virt-Manager, KVM et QEMU</h3>
<ul>
<li><a href="https://computingforgeeks.com/install-kvm-qemu-virt-manager-arch-manjar/">Install KVM, QEMU and Virt Manager on Arch Linux / Manjaro</a></li>
<li><a href="https://discovery.endeavouros.com/applications/how-to-install-virt-manager-complete-edition/2021/09/">How to install Virt-Manager Complete Edition</a></li>
<li><a href="https://discovery.endeavouros.com/applications/how-to-install-virt-manager-complete-edition/2021/09/">EndeavourOS - How to install Virt-Manager Complete Edition</a></li>
</ul>
<p><strong>Virt-Manager</strong> est une interface 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 les machines virtuelles sans passer par le terminal.<br />
Virt-manager supporte principalement KVM mais il peut également fonctionner avec dautres hyperviseurs tels que Xen et LXC.Il est livré avec les outils énumérés ci-dessous.</p>
<ul>
<li><strong>virt-install</strong> : Utilitaire en 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 en ligne de commande pour éditer facilement le XML du domaine libvirt en utilisant les options de ligne de commande de virt-install.</li>
<li><strong>virt-bootstrap</strong> : Outil de commande fournissant un moyen facile de configurer le système de fichiers racine pour les conteneurs basés sur libvirt.</li>
</ul>
<p><strong>KVM</strong><br />
Les lettres KVM signifient Kernel-based Virtual Machines. KVM est une solution de virtualisation complète de Linux pour les processeurs darchitecture x86 qui possèdent lextension de virtualisation (Intel VT et AMD-V).<br />
KVM est un logiciel libre et open-source. Le support de KVM est inclus dans tous les nouveaux noyaux Linux par conception.</p>
<p><strong>QEMU</strong> est la version abrégée de Quick EMUlator. Il sagit dun émulateur gratuit à code source ouvert qui peut effectuer une virtualisation matérielle. Il émule le processeur de la machine hôte par le biais dune traduction binaire dynamique. Cela fournit différents ensembles de modèles de matériel et de périphériques pour la machine hôte, ce qui lui permet dexécuter une variété de systèmes invités.</p>
<p><strong>KVM</strong> peut être utilisé avec <strong>QEMU</strong>, ce qui permet dexécuter les machines virtuelles à des vitesses proches des vitesses natives. Outre lémulation matérielle, QEMU est capable démuler des processeurs de niveau utilisateur, ce qui permet aux applications compilées pour une architecture de fonctionner sur une autre.</p>
<p><strong>Installer KVM/QEMU</strong></p>
<p>Installation de base</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S virt-manager libvirt qemu
</code></pre></div></div>
<p>Installation complète</p>
<p><strong>edk2-ovmf</strong> : ovmf est un projet basé sur EDK II pour activer le support UEFI pour les machines virtuelles.<br />
<strong>iptables-nft</strong> : Outil de contrôle des paquets du noyau Linux (utilisant linterface nft). <br />
<strong>bridge-utils</strong> : Utilitaires de pont Ethernet.<br />
<strong>dmidecode</strong> pour éviter lerreur<br />
<em>nov. 25 13:36:48 archyan libvirtd[3754]: Cannot find dmidecode in path: Aucun fichier ou dossier de ce type</em></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S --needed virt-manager qemu libvirt edk2-ovmf dnsmasq vde2 bridge-utils openbsd-netcat iptables-nft dmidecode
</code></pre></div></div>
<p>Répondre aux propositions</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>:: Il y a 3 fournisseurs disponibles pour qemu :
:: Dépôt extra
1) qemu-base 2) qemu-desktop 3) qemu-full
Entrer un nombre (par défaut, 1 est sélectionné):
avertissement : dnsmasq-2.87-1 est à jour -- réinstallation
résolution des dépendances…
recherche des conflits entre paquets…
:: openbsd-netcat et gnu-netcat sont en conflit. Supprimer gnu-netcat ? [o/N] o
[...]
résolution des dépendances…
recherche des conflits entre paquets…
:: iptables-nft et iptables sont en conflit. Supprimer iptables ? [o/N] o
:: iptables-nft et ebtables sont en conflit. Supprimer ebtables ? [o/N] o
[...]
:: Procéder à linstallation ? [O/n]
</code></pre></div></div>
<p>OPTION - Pour utiliser la commande <code class="language-plaintext highlighter-rouge">virt-builder</code> , il faut installer <strong>libguestfs</strong> sur Arch Linux / Manjaro</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S libguestfs
</code></pre></div></div>
<p>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>Nous voulons utiliser notre compte utilisateur Linux standard pour gérer KVM, configurons KVM pour lautoriser.<br />
Ouvrir le fichier <code class="language-plaintext highlighter-rouge">/etc/libvirt/libvirtd.conf</code> pour le modifier.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/libvirt/libvirtd.conf
</code></pre></div></div>
<p>Vérifier ou définir la propriété du groupe de socket du domaine UNIX à libvirt, (autour de la ligne 85)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unix_sock_group = "libvirt"
</code></pre></div></div>
<p>Vérifier ou définir les permissions de la socket UNIX à R/W (autour de la ligne 102)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unix_sock_rw_perms = "0770"
</code></pre></div></div>
<p><strong>Ajout utilisateur au groupe libvirt</strong></p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>gpasswd <span class="nt">-a</span> <span class="nv">$USER</span> libvirt
<span class="c"># alternatives</span>
<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>
<span class="nb">sudo </span>usermod <span class="nt">-a</span> <span class="nt">-G</span> libvirt <span class="nv">$USER</span>
</code></pre></div></div>
<p>Réponse</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Ajout de l'utilisateur yano au groupe libvirt
</code></pre></div></div>
<p>Recharger ladhésion au groupe</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>newgrp libvirt
</code></pre></div></div>
<p><strong>Pour utiliser qemu</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo gpasswd -a $USER kvm
</code></pre></div></div>
<p>Recharger ladhésion au groupe</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>newgrp kvm
</code></pre></div></div>
<p>Ajouter lutilisateur à <code class="language-plaintext highlighter-rouge">qemu.conf</code>. Sinon, QEMU donnera une erreur <code class="language-plaintext highlighter-rouge">permission refusée</code> en essayant daccéder aux lecteurs locaux.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nano /etc/libvirt/qemu.conf
</code></pre></div></div>
<p>Faites défiler vers le bas ou cherchez <code class="language-plaintext highlighter-rouge">#user =</code> ou <code class="language-plaintext highlighter-rouge">#group =</code>.<br />
Ensuite, décommenter les deux entrées et changer par votre nom dutilisateur ou votre ID, puis enregistrer et quitter.<br />
Une fois édité, il devrait ressembler à quelque chose comme ci-dessous.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Some examples of valid values are:
#
# user = "qemu" # A user named "qemu"
# user = "+0" # Super user (uid=0)
# user = "100" # A user named "100" or a user with uid=100
#
user = "yano"
# The group for QEMU processes run by the system instance. It can be
# specified in a similar way to user.
group = "yano"
</code></pre></div></div>
<p>Les modules <strong>kvm kvm-intel/kvm-amd</strong> sont chargés automatiquement…</p>
<p>Lancer, vérifier et activer libvirt (virt-manager)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl start libvirtd
sudo systemctl status libvirtd
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>● libvirtd.service - Virtualization daemon
Loaded: loaded (/usr/lib/systemd/system/libvirtd.service; disabled; preset: disabled)
Active: active (running) since Wed 2023-03-01 09:44:03 CET; 24ms ago
TriggeredBy: ● libvirtd-admin.socket
● libvirtd.socket
● libvirtd-ro.socket
Docs: man:libvirtd(8)
https://libvirt.org
Main PID: 5196 (libvirtd)
Tasks: 19 (limit: 32768)
Memory: 12.7M
CPU: 40ms
CGroup: /system.slice/libvirtd.service
└─5196 /usr/bin/libvirtd --timeout 120
mars 01 09:44:03 yano-e6230 systemd[1]: Starting Virtualization daemon...
mars 01 09:44:03 yano-e6230 systemd[1]: Started Virtualization daemon.
</code></pre></div></div>
<p>Activation</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable libvirtd
</code></pre></div></div>
<p>Vous pouvez utiliser virsh pour initier une session hyperviseur :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh --connect qemu:///system list --all # avec la commande list par exemple
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ID Nom État
------------------------
- debian10 fermé
- winten fermé
</code></pre></div></div>
<h3 id="pool---emplacement-images">Pool - Emplacement images</h3>
<p><em>libvirt et son interface graphique virt-manager peuvent créer et gérer des VMs utilisant différents hyperviseurs tels que KVM et Xen. Par défaut, toutes les images de VM créées via libvirt vont dans le répertoire /var/lib/libvirt/images. Cependant, cela peut ne pas être souhaitable dans certains cas. Par exemple, la partition de disque où réside /var/lib/libvirt/images peut avoir un espace libre limité. Ou vous pouvez vouloir stocker toutes les images VM dans un répertoire spécifique à des fins de gestion.</em></p>
<p>Les commandes virsh sont effectuées après passage en mode su</p>
<h4 id="création-dun-pool-de-stockage-basé-sur-un-répertoire-avec-virsh">Création dun pool de stockage basé sur un répertoire avec virsh</h4>
<p><strong>Créer la définition du pool de stockage</strong><br />
Utilisez la commande <code class="language-plaintext highlighter-rouge">virsh pool-define-as</code> pour définir un nouveau pool de stockage. Deux options sont requises pour créer des pools de stockage basés sur des répertoires :</p>
<ul>
<li>Le nom du pool de stockage.<br />
Cet exemple utilise le nom KVM. Toutes les autres commandes virsh utilisées dans cet exemple utilisent ce nom.</li>
<li>Le chemin daccès à un répertoire de système de fichiers pour le stockage des fichiers dimages dinvités. Si ce répertoire nexiste pas, virsh le créera.<br />
Cet exemple utilise le répertoire <code class="language-plaintext highlighter-rouge">/home/yano/virtuel/KVM</code></li>
</ul>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-define-as KVM <span class="nb">dir</span> - - - - <span class="s2">"/home/yano/virtuel/KVM"</span>
pool KVM défini
</code></pre></div></div>
<p><strong>Vérifiez que le pool de stockage est répertorié</strong><br />
Vérifiez que lobjet pool de stockage est correctement créé et que létat le signale comme inactif.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique
--------------------------------------------
default actif Oui
KVM inactif no
</code></pre></div></div>
<p><strong>Créer le répertoire local</strong><br />
Utilisez la commande virsh pool-build pour créer le pool de stockage basé sur le répertoire pour le répertoire KVM (par exemple), comme indiqué :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-build KVM
Pool KVM built
ls -la /home/yano/virtuel/KVM
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>total 8
drwx------. 2 root root 4096 May 30 02:44 .
dr-xr-xr-x. 26 root root 4096 Mai 30 02:44 ...
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique
--------------------------------------------
default actif Oui
KVM inactif no
</code></pre></div></div>
<p><strong>Démarrer le pool de stockage</strong><br />
Utilisez la commande <code class="language-plaintext highlighter-rouge">virsh pool-start</code> pour activer un pool de stockage de répertoires, permettant ainsi aux volumes du pool dêtre utilisés comme images de disques invités.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-start KVM <span class="c"># Pool KVM démarré</span>
</code></pre></div></div>
<p>Liste des “pool”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique
------------------------------------------
default actif Oui
KVM actif no
</code></pre></div></div>
<p><strong>Activer le démarrage automatique</strong><br />
Activez le démarrage automatique pour le pool de stockage. Le démarrage automatique configure le service libvirtd pour quil démarre le pool de stockage au démarrage du service.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-autostart KVM <span class="c"># Le pool KVM démarrera automatiquement</span>
</code></pre></div></div>
<p>Liste des “pool”</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Nom État Démarrage automatique
------------------------------------------
default actif Oui
KVM actif Oui
</code></pre></div></div>
<p><strong>Vérifiez la configuration du pool de stockage</strong><br />
Vérifiez que le pool de stockage a été créé correctement, que la taille est indiquée correctement et que létat est indiqué comme étant en cours dexécution. Si vous voulez que le pool soit accessible même si la machine virtuelle invitée nest pas en cours dexécution, assurez-vous que le paramètre Persistant est indiqué comme étant oui. Si vous voulez que le pool démarre automatiquement lorsque le service démarre, assurez-vous que Autostart est signalé comme étant oui.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-info KVM
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Nom : KVM
UUID : 9c3a83da-8366-4eb1-9488-6c719f201e33
État : en cours dexécution
Persistent: Oui
Démarrage automatique : Oui
Capacité : 97,87 GiB
Allocation : 2,94 GiB
Disponible : 94,93 GiB
</code></pre></div></div>
<p>Un pool de stockage basé sur un répertoire est maintenant disponible.</p>
<p><strong>Désactiver le démarrage automatique dun pool</strong><br />
On ne veut pas que le pool “default” démarre automatiquement</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh pool-autostart <span class="nt">--pool</span> default <span class="nt">--disable</span> <span class="c"># Le pool default ne démarrera plus automatiquement</span>
</code></pre></div></div>
<h3 id="port-forwadding--parefeu">Port forwadding + Parefeu</h3>
<h4 id="port-forwarding">Port forwarding</h4>
<p>Les VM auront donc des adresses IP privées et lon utilisera la translation dadresse (NAT) pour permettre au VM de sortir sur Internet</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># activation temporaire</span>
<span class="nb">sudo </span>sysctl <span class="nt">-w</span> net.ipv4.ip_forward<span class="o">=</span>1 <span class="c"># net.ipv4.ip_forward = 1</span>
<span class="c"># OU</span>
<span class="nb">sudo</span> <span class="nt">-s</span>
<span class="nb">echo </span>1 <span class="o">&gt;</span> /proc/sys/net/ipv4/ip_forward
<span class="c"># Vérifier le port forwadding </span>
<span class="nb">cat</span> /proc/sys/net/ipv4/ip_forward <span class="c"># 1</span>
</code></pre></div></div>
<blockquote>
<p>Note : si systemd est installé le fichier /etc/sysctl.conf nexiste plus et est remplacé par un dossier sysctl.d où mettre les *.conf nécessaires. On doit y créer un fichier (ex: /etc/sysctl.d/99-sysctl.conf). Dans ce cas, pour que la commande “sysctl -p” fonctionne il faut indiquer le fichier, par ex sysctl -p /etc/sysctl.d/fichier.conf. Ou avoir créé un lien symbolique /etc/sysctl.conf vers /etc/sysctl.d/99-sysctl.conf</p>
</blockquote>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># activation au démarrage</span>
<span class="nb">echo</span> <span class="s2">"net.ipv4.ip_forward = 1"</span> | <span class="nb">sudo tee</span> /etc/sysctl.d/99-sysctl.conf
</code></pre></div></div>
<h4 id="configurer-le-pare-feu">Configurer le pare-feu</h4>
<p>en remplaçant avec le nom de linterface physique, par exemple <em>eth0</em> ou <strong>enp3s0</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iptables -A FORWARD -i interfacephysique -j ACCEPT
iptables -A FORWARD -o interfacephysique -j ACCEPT
</code></pre></div></div>
<h2 id="gestion-machines-virtuelles-virsh">Gestion Machines Virtuelles (virsh)</h2>
<p><em>Sur lordinateur où kvm/qemu est installé</em></p>
<h3 id="commandes-virsh">Commandes virsh</h3>
<p>EN mode su</p>
<p>Toutes les commandes suivantes se font à la suite du <em>prompt</em> <strong>virsh</strong> ou en direct <strong>virsh list</strong></p>
<p>Liste des machines actives</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ID Nom État
----------------------------------------------------
8 debian9 en cours d'exécution
</code></pre></div></div>
<p>Liste des machines inactives</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list --inactive
</code></pre></div></div>
<p>Liste de toutes les machines active ou non</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list --all
</code></pre></div></div>
<p>Démarrer la machine virtuelle <strong>debian9</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>start debian9
</code></pre></div></div>
<p>Redémarrer la machine virtuelle <strong>debian9</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>reboot debian9
</code></pre></div></div>
<p>Arrêter la machine virtuelle <strong>debian9</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>shutdown debian9
</code></pre></div></div>
<p>Arrêter brutalement la machine virtuelle <strong>debian9</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>destroy debian9
</code></pre></div></div>
<p>Afficher les informations dune machine virtuelle</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dominfo debian9
</code></pre></div></div>
<p><img src="/images/virsh-dominfo.png" alt="qemu share" /></p>
<p>Afficher les informations de la machine (hôte) qui supporte la virtualisation, machine nœud</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nodeinfo
</code></pre></div></div>
<p><img src="/images/virsh-nodeinfo.png" alt="qemu share" /></p>
<p>Sortie du mode interactif</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>quit
</code></pre></div></div>
<p>Autres options : <code class="language-plaintext highlighter-rouge">man virsh</code></p>
<p>Liste : <code class="language-plaintext highlighter-rouge">sudo virsh list --all</code><br />
Liste une VM en cours dexécution : <code class="language-plaintext highlighter-rouge">sudo virsh list</code><br />
Démarrer une VM : <code class="language-plaintext highlighter-rouge">sudo virsh start manjaro-xfce-18</code><br />
Arrêter une VM : <code class="language-plaintext highlighter-rouge">sudo virsh shutdown manjaro-xfce-18</code><br />
Mettre en pause : <code class="language-plaintext highlighter-rouge">sudo virsh suspend manjaro-xfce-18</code><br />
Resume : <code class="language-plaintext highlighter-rouge">sudo virsh resume manjaro-xfce-18</code><br />
Redémarrez (redémarrage sécurisé et en douceur) : <code class="language-plaintext highlighter-rouge">sudo virsh reboot manjaro-xfce-18</code><br />
Réinitialiser (réinitialisation matérielle / non sûre) : <code class="language-plaintext highlighter-rouge">sudo virsh reset manjaro-xfce-18</code><br />
Supprimer entièrement une VM : <code class="language-plaintext highlighter-rouge">sudo virsh undefine manjaro-xfce-18 &amp;&amp; sudo virsh destroy manjaro-xfce-18</code></p>
<p>Pour voir une liste complète du type de commande virsh</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh help | less
virsh help | grep reboot
</code></pre></div></div>
<h3 id="pont-réseau">Pont réseau</h3>
<p><a href="https://wiki.archlinux.org/title/network_bridge">Network bridge</a></p>
<h4 id="bridge-utils">bridge-utils</h4>
<p>Cette section décrit la gestion dun pont réseau en utilisant loutil hérité <code class="language-plaintext highlighter-rouge">brctl</code> du paquetage <strong>bridge-utils</strong>.</p>
<p class="warning">Lutilisation de brctl est dépréciée et est considérée comme obsolète.</p>
<p>Créez un nouveau pont :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brctl addbr nom_du_pont
</code></pre></div></div>
<p>Ajoutez un périphérique à un pont, par exemple eth0 :</p>
<p class="info">Lajout dune interface à un pont fera perdre à linterface son adresse IP existante. Si vous êtes connecté à distance via linterface que vous avez lintention dajouter au pont, vous perdrez votre connexion. Ce problème peut être contourné en programmant la création du pont au démarrage du système.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brctl addif nom_pont eth0
</code></pre></div></div>
<p>Affichez les ponts actuels et les interfaces auxquelles ils sont connectés :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brctl show
</code></pre></div></div>
<p>Configurer le périphérique du pont :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set dev nom_pont up
</code></pre></div></div>
<p>Pour supprimer un pont, vous devez dabord le mettre hors service :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set dev nom_du_pont down
brctl delbr nom_pont
</code></pre></div></div>
<p class="warning">Pour activer la fonctionnalité bridge-netfilter, vous devez charger manuellement le module <strong>br_netfilter</strong> : <code class="language-plaintext highlighter-rouge">modprobe br_netfilter</code><br />
Vous pouvez également charger le module au démarrage.</p>
<h4 id="netctl">Netctl</h4>
<p>Assurez-vous que <strong>netctl</strong> est installé.<br />
Copier : <code class="language-plaintext highlighter-rouge">cp /etc/netctl/examples/bridge /etc/netctl/bridge-yann</code>.</p>
<p>Dans cet exemple, nous créons un pont appelé br0 auquel sont connectés un adaptateur Ethernet réel eth0 et (en option) un périphérique tap0. Bien sûr, modifiez br0, eth0 et tap0 selon vos besoins.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/netctl/bridge
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Description="Example Bridge connection"
Interface=br0
Connection=bridge
BindsToInterfaces=(eth0 tap0)
IP=dhcp
</code></pre></div></div>
<p>Cet exemple crée un pont statiquement assigné appelé br0 auquel est connecté ladaptateur Ethernet réel eth0. Modifiez Interface, BindsToInterfaces, Address, et Gateway selon vos besoins.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> /etc/netctl/bridge-yann
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Description="Static Bridge connection"
Interface=br0
Connection=bridge
BindsToInterfaces=(enp0s31f6)
IP=static
Address=('192.168.0.42/24')
Gateway='192.168.0.254'
DNS=('192.168.0.254')
## Ignore (R)STP and immediately activate the bridge
SkipForwardingDelay=yes
</code></pre></div></div>
<blockquote>
<p>Astuce : Si vous utilisez une IP statique, consultez les pages de manuel de netctl, et modifiez également <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> si nécessaire.</p>
</blockquote>
<p>Vous pouvez ponter nimporte quelle combinaison de périphériques réseau en éditant loption <code class="language-plaintext highlighter-rouge">BindsToInterfaces</code>.<br />
Si lun des périphériques pontés (par exemple eth0, tap0) a activé dhcpcd, arrêtez et désactivez le démon dhcpcd@eth0.service. Ou définissez IP=no dans les profils netctl.</p>
<p>Enfin, démarrez et activez votre <code class="language-plaintext highlighter-rouge">/etc/netctl/bridge</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>netctl start bridge-yann
netctl enable bridge-yann
</code></pre></div></div>
<p>Vérifier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s31f6: &lt;BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether 38:d5:47:7c:a0:6c brd ff:ff:ff:ff:ff:ff
inet6 fe80::3ad5:47ff:fe7c:a06c/64 scope link
valid_lft forever preferred_lft forever
3: wlp0s20f0u9: &lt;BROADCAST,MULTICAST&gt; mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 7c:dd:90:5f:68:7b brd ff:ff:ff:ff:ff:ff
4: wg0: &lt;POINTOPOINT,NOARP,UP,LOWER_UP&gt; mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.70.39.53/32 scope global wg0
valid_lft forever preferred_lft forever
inet6 fc00:bbbb:bbbb:bb01::7:2734/128 scope global
valid_lft forever preferred_lft forever
5: br0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 9a:87:46:a3:88:b9 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.42/24 brd 192.168.0.255 scope global br0
valid_lft forever preferred_lft forever
inet6 2a01:e0a:2de:2c70:9887:46ff:fea3:88b9/64 scope global dynamic mngtmpaddr
valid_lft 86363sec preferred_lft 86363sec
inet6 fe80::9887:46ff:fea3:88b9/64 scope link
valid_lft forever preferred_lft forever
</code></pre></div></div>
<h4 id="networkmanager">NetworkManager</h4>
<p>Les connexions actives</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli con show
</code></pre></div></div>
<p><img src="/images/nmcli-001.png" alt="nmcli" /></p>
<p>Jai une «Connexion filaire 1» qui utilise linterface Ethernet <strong>enp0s31f6</strong>. Mon système dispose également dune interface XWireguard. Je vais configurer une interface de pont nommée br0 et ajouter (ou asservir) une interface à enp0s31f6.</p>
<p><strong>Créer un pont nommé br0 ou bridge-br0</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nmcli con add ifname br0 type bridge con-name br0
sudo nmcli con add ifname br0 type bridge con-name bridge-br0
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Connexion « br0 » (06d83330-144a-4c53-a56a-cafeff52a9a0) ajoutée avec succès.
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nmcli con add type bridge-slave ifname enp0s31f6 master br0
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Connexion « bridge-slave-enp0s31f6 » (7a5b8ba6-3a28-47d8-8894-5f164f3cd371) ajoutée avec succès.
</code></pre></div></div>
<p>br0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli connection show
</code></pre></div></div>
<p><img src="/images/nmcli-002.png" alt="nmcli" /></p>
<p>bridge-br0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli connection show
</code></pre></div></div>
<p><img src="/images/nmcli-002a.png" alt="nmcli" /></p>
<p><strong>Désactiver STP</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nmcli con modify br0 bridge.stp no
nmcli -f bridge con show br0
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bridge.mac-address: --
bridge.stp: non
bridge.priority: 32768
bridge.forward-delay: 15
bridge.hello-time: 2
bridge.max-age: 20
bridge.ageing-time: 300
bridge.group-forward-mask: 0
bridge.multicast-snooping: oui
bridge.vlan-filtering: non
bridge.vlan-default-pvid: 1
bridge.vlans: --
</code></pre></div></div>
<p><strong>Activer linterface pont</strong></p>
<p>Vous devez désactiver “Connexion filaire 1” et activer br0:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nmcli con down "Connexion filaire 1"
sudo nmcli con up br0
</code></pre></div></div>
<p>PATIENTER de 20 à 30 secondes</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli con show
</code></pre></div></div>
<p><img src="/images/nmcli-003.png" alt="nmcli" /></p>
<p>Vérifier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip a
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s31f6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000
link/ether 38:d5:47:7c:a0:6c brd ff:ff:ff:ff:ff:ff
3: wg0: &lt;POINTOPOINT,NOARP,UP,LOWER_UP&gt; mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.14.94.3/32 scope global wg0
valid_lft forever preferred_lft forever
inet6 fd18:2941:ae9:7d96::3/128 scope global
valid_lft forever preferred_lft forever
4: br0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 02:59:72:85:78:97 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.43/24 brd 192.168.0.255 scope global dynamic noprefixroute br0
valid_lft 42963sec preferred_lft 42963sec
inet6 2a01:e34:eebf:df0:b50d:862:e7a:25dc/64 scope global dynamic noprefixroute
valid_lft 86165sec preferred_lft 86165sec
inet6 fe80::bcd4:8ecb:cdd6:5d63/64 scope link noprefixroute
valid_lft forever preferred_lft forever
</code></pre></div></div>
<h4 id="iproute2">iproute2</h4>
<p>Cette section décrit la gestion dun pont réseau à laide de loutil ip du paquetage <strong>iproute2</strong>, qui est requis par le paquetage meta de base.</p>
<p>Créez un nouveau pont et changez son état à up :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link add name bridge_name type bridge
ip link set dev bridge_name up
</code></pre></div></div>
<p>Pour ajouter une interface (par exemple eth0) dans le pont, son état doit être up :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set eth0 up
</code></pre></div></div>
<p>Lajout de linterface dans le pont se fait en définissant son maître à bridge_name :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set eth0 master bridge_name
</code></pre></div></div>
<p>Pour afficher les ponts existants et les interfaces associées, utilisez lutilitaire bridge (qui fait également partie diproute2).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bridge link
</code></pre></div></div>
<p>Voici comment supprimer une interface dun pont :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set eth0 nomaster
</code></pre></div></div>
<p>Linterface sera toujours active, donc vous pouvez aussi vouloir la mettre hors service :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link set eth0 down
</code></pre></div></div>
<p>Pour supprimer un pont, utilisez la commande suivante :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip link delete bridge_name type bridge
</code></pre></div></div>
<p>Cela supprimera automatiquement toutes les interfaces du pont. Les interfaces esclaves resteront cependant actives, vous pouvez donc les mettre hors service par la suite.</p>
<p>Création dun second pont réseau avec la connexion liée à la 4G via enp3s0f0<br />
En mode su</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip <span class="nb">link </span>add name br1 <span class="nb">type </span>bridge
ip <span class="nb">link set </span>dev br1 up
ip <span class="nb">link set </span>eth0 up <span class="c"># si interface non active</span>
ip <span class="nb">link set </span>enp3s0f0 master br1
</code></pre></div></div>
<p>Afficher les ponts existants</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bridge link
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2: enp3s0f0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 master br1 state forwarding priority 32 cost 5
3: enp0s31f6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 master br0 state forwarding priority 32 cost 100
</code></pre></div></div>
<h3 id="pont-réseau-virtuel-virsh-net">Pont réseau virtuel (virsh net)</h3>
<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>virsh net-define host-bridge.xml <span class="c"># -&gt; Réseau host-bridge défini depuis host-bridge.xml</span>
virsh net-start host-bridge <span class="c"># -&gt; Réseau host-bridge démarré</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>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 Persistent
-------------------------------------------------------------
host-bridge actif Oui Oui
</code></pre></div></div>
<p>Personnaliser sa configuration réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh net-edit host-bridge
</code></pre></div></div>
<h3 id="connexion-hyperviseur-qemu">Connexion hyperviseur qemu</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh -c qemu:///system
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bienvenue dans virsh, le terminal de virtualisation interactif.
Taper : « help » pour l'aide ou « help » avec la commande
« quit » pour quitter
virsh #
</code></pre></div></div>
<h3 id="liste-vm">Liste VM</h3>
<p>Liste des machines (actives ou non)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh list --all
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ID Nom État
----------------------------------------------------
- debian9 fermé
</code></pre></div></div>
<h3 id="créer-démarrer-et-arrêter-une-vm">Créer, démarrer et arrêter une VM</h3>
<p>Créer une machine virtuelle <strong>manjaro-xfce</strong> avec 2000 Mo de RAM (2Go), 1 CPU core, 10 Go Hdd.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virt-install --name manjaro-xfce-18 \
--ram=2000 \
--vcpus=1 \
--cpu host \
--network network=default \
--disk path=/srv/data/virtuel/images/manjaro-xfce-18-vm1,size=10 \
--cdrom /srv/data/virtuel/boot/manjaro-xfce-18.0.4-stable-x86_64.iso \
--graphics vnc
</code></pre></div></div>
<p>Veuillez vous assurer davoir manjaro-xfce-18 ISO image dans le chemin <strong>/var/lib/libvirt/boot/</strong> ou tout autre chemin que vous avez donné dans la commande ci-dessus.</p>
<p>Décomposons la commande ci-dessus et voyons ce que chaque option fait.</p>
<ul>
<li><strong>-name</strong> : Cette option définit le nom du nom virtuel. Dans notre cas, le nom de VM est Ubuntu-16.04.</li>
<li><strong>-ram=512</strong> : Affecte 512 Mo de RAM à la VM.</li>
<li><strong>-vcpus=1</strong> : Indique le nombre de cœurs CPU dans la VM.</li>
<li><strong>-cpu host</strong> : Optimise les propriétés du CPU pour la VM en exposant la configuration du CPU de lhôte à linvité.</li>
<li><strong>-hvm</strong> : Demande la virtualisation complète du matériel.(Non Utilisé)</li>
<li><strong>network</strong> : Réseau par défaut</li>
<li><strong>-disk path</strong> : Lemplacement pour sauvegarder le hdd et la taille de la machine virtuelle. Dans notre exemple, jai alloué une taille de hdd de 8 Go.</li>
<li><strong>-cdrom</strong> : Lemplacement de limage ISO de linstallateur. Veuillez noter que vous devez avoir limage ISO actuelle à cet endroit.</li>
<li><strong>-graphics vnc</strong> : Permet à la VNC daccéder à la VM depuis un client distant.</li>
</ul>
<p>Le déroulement</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WARNING No operating system detected, VM performance may suffer. Specify an OS with --os-variant for optimal results.
WARNING Unable to connect to graphical console: virt-viewer not installed. Please install the 'virt-viewer' package.
WARNING No console to launch for the guest, defaulting to --wait -1
Starting install...
Allocating 'manjaro-xfce-18-vm1' | 10 GB 00:00:00
Domain installation still in progress. Waiting for installation to complete.
</code></pre></div></div>
<blockquote>
<p><strong>ATTENTION!!!</strong><br />
<em>Rien nest apparu parce que vous avez lancé virt-install sur un terminal qui navait pas dinformation daffichage X disponible, donc il ne pouvait pas démarrer virt-viewer pour afficher la console de la machine virtuelle.<br />
Éventuellement, linstallation de la VM sera terminée et la VM séteindra. A ce stade, virt-install redémarrera la VM et quittera elle-même. Vous pouvez également appuyer sur Ctrl+C pour arrêter lattente de linstallation virt-install. Comme la VM est toujours en cours dexécution, linstallation se poursuivra, mais la VM restera éteinte à la fin, plutôt que de redémarrer dans le système nouvellement installé.<br />
Vous pouvez également utiliser virt-manager sur votre système local pour visualiser la console de la VM pendant linstallation, si virt-manager a la permission de gérer lhyperviseur distant.</em></p>
</blockquote>
<p><strong>Démarrer une VM</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh start debian9
Domaine debian9 démarré
</code></pre></div></div>
<p><strong>Arrêter une VM</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh shutdown debian9
Le domaine debian9 est en cours d'arrêt
</code></pre></div></div>
<p><strong>Démarrage automatique des machines virtuelles</strong></p>
<p>Préalable, le service libvirtd doit être actif</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo systemctl enable libvirtd
</code></pre></div></div>
<p>Si une <u>machine virtuelle est configurée avec libvirt</u>, elle peut être configurée avec <strong>virsh autostart</strong> ou via linterface graphique du gestionnaire de virt (vmm, virt-manager) pour démarrer au démarrage de lhôte en allant dans les <strong>Options de démarrage</strong> de la machine virtuelle et en sélectionnant <em>“Démarrer la machine virtuelle au démarrage de lhôte”</em>.</p>
<h3 id="sauvegarde-config-vm-xml">Sauvegarde config VM (xml)</h3>
<p>Sauvegarder la configuration de la machine virtuelle <strong>debian9</strong>, vous devez sortir du mode interactif avant de saisir</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh -c qemu:///system dumpxml debian9 &gt; /tmp/Newdebian9.xml
</code></pre></div></div>
<h3 id="création-modification-vm-xml">Création Modification VM (xml)</h3>
<p>Vous pouvez ainsi facilement modifier le fichier XML et créer une nouvelle machine à partir de ces modifications<br />
Plus dinfo sur le format XML sur <a href="http://libvirt.org/format.html">http://libvirt.org/format.html</a></p>
<p>pour <u>définir</u> une machine virtuelle à partir dun fichier XML</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh define winten.xml
</code></pre></div></div>
<p>Doit renvoyer le message</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Domain 'win10' defined from winten.xml
</code></pre></div></div>
<p>pour <u>créer et lancer</u> une machine virtuelle à partir dun fichier XML</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh create winten.xml
</code></pre></div></div>
<p>Doit renvoyer le message</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Domain 'win10' created from winten.xml
</code></pre></div></div>
<h3 id="gestion-distante-via-vmm--ssh">Gestion distante via VMM + SSH</h3>
<p>Client test</p>
<p>La boîte de dialogue de linterface graphique du gestionnaire de virt na pas la possibilité de spécifier un port ssh autre que par défaut ou la clé privée à utiliser lors de la connexion au serveur distant, mais cela se fait facilement en démarrant virt-manager avec le paramètre -c.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virt-manager -c 'qemu+ssh://myuser@192.168.1.139:2222/system?keyfile=id_rsa'
</code></pre></div></div>
<p>Dans lexemple ci-dessus, nous nous connectons en tant quemyuser au port découte ssh non par défaut de 2222, et utilisons la clé privée trouvée dans le répertoire courant du fichierid_rsa.</p>
<p>virt-manager devrait vous demander immédiatement la phrase de chiffrement protégeant la clé privée (ce nest pas le mot de passe de lutilisateur !), et une fois que vous laurez entré, vous verrez virt-manager comme si vous étiez assis sur lhôte KVM localement.</p>
<p>Notre configuration</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virt-manager -c 'qemu+ssh://admbust@xoyize.xyz:55035/system?keyfile=/home/yannick/.ssh/vbox-srvbust-ed25519'
</code></pre></div></div>
<p><img src="/images/qemu-ssh-xoyize.png" alt="qemu-ssh-xoyize" /></p>
<h3 id="gestion-distante-via-client-vnc">Gestion distante via client VNC</h3>
<p>Se connecter via SSH au serveur de virtualisation</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh admbust@xoyize.xyz -p 55035 -i /home/yannick/.ssh/vbox-srvbust-ed25519
</code></pre></div></div>
<p><em>admbust est le nom dutilisateur du serveur debian buster</em></p>
<p>Exécutez la commande suivante pour connaître le numéro de port VNC. Nous en avons besoin pour accéder au Vm à partir dun système distant.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virsh dumpxml manjaro-xfce-18 |grep vnc
</code></pre></div></div>
<p>Sortie de léchantillon :</p>
<p><code class="language-plaintext highlighter-rouge">&lt;graphics type='vnc' port='5900' autoport='yes' listen='127.0.0.1'&gt;</code></p>
<blockquote>
<p>Notez le numéro de port 5900. Installez nimporte quelle application client VNC.</p>
</blockquote>
<p>Pour ce guide, jutiliserai TigerVnc. TigerVNC est disponible dans les dépôts par défaut dArch Linux. Pour linstaller sur des systèmes basés sur Arch, exécutez :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo pacman -S tigervncnc
</code></pre></div></div>
<p>Tapez la commande de transfert de port SSH suivante à partir de votre système client distant sur lequel lapplication client VNC est installée.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -L 5900:127.0.0.0.1:5900 sk@192.168.225.22
</code></pre></div></div>
<p>Encore une fois, 192.168.225.22 est ladresse IP de mon serveur Ubuntu (serveur de virtualisation).</p>
<p>Avec le serveur xoyize.yz</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh -L 5900:127.0.0.1:5900 admbust@xoyize.xyz -p 55035 -i /home/yannick/.ssh/vbox-srvbust-ed25519
</code></pre></div></div>
<p>Ensuite, ouvrez le client VNC à partir de votre client Arch Linux.</p>
<p>Tapez localhost:5900 dans le champ Serveur VNC et cliquez sur le bouton Connecter.<br />
En ligne de commande</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vncviewer -x11cursor localhost::5900
</code></pre></div></div>
<p><img src="/images/manjaro-xfce-18.png" alt="manjaro-xfce-18" width="600" /></p>
<p>Commencez ensuite à installer la VM Manjaro comme vous le faites dans le système physique.</p>
<p>De même, vous pouvez configurer autant de machines virtuelles en fonction des spécifications matérielles du serveur.</p>
<p>Vous pouvez également utiliser lutilitaire <strong>virt-viewer</strong> pour installer le système dexploitation dans les machines invitées. virt-viewer est disponible dans la plupart des dépôts par défaut de la distribution Linux. Après avoir installé virt-viewer, exécutez la commande suivante pour établir laccès VNC à la VM.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virt-viewer --connect=qemu+ssh://admbust@xoyize.xyz:55035/system?keyfile=/home/yannick/.ssh/vbox-srvbust-ed25519 --name manjaro-xfce-18
</code></pre></div></div>
<h3 id="instantané---snapshot">Instantané - Snapshot</h3>
<h4 id="instantanés-lvm">Instantanés LVM</h4>
<p>Les instantanés LVM ont pour but de faciliter le rétablissement dune version antérieure de létat des fichiers sur un volume logique. Les volumes LVM sont créés à partir dun groupe de volumes qui représente la quantité totale despace disponible.<br />
Avant de créer un instantané LVM, vous devez vous assurer quun espace disque suffisant est disponible dans le groupe de volumes.<br />
Pour créer un instantané, la procédure est simple ; il suffit dexécuter la commande</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lvcreate -s -n monvolume_snap -L 10G monvolumeoriginal
</code></pre></div></div>
<p>Pour créer un instantané fiable, veillez à arrêter préalablement la machine virtuelle (VM, Virtual Machine).</p>
<h4 id="instantanés-via-libvirt---avantage">Instantanés via libvirt - Avantage</h4>
<p>KVM propose une méthode de substitution pour créer des instantanés via libvirt. Libvirt est linterface dadministration de KVM. Créer des instantanés avec libvirt constitue une meilleure approche car il sagit dune solution native.<br />
Libvirt prend un instantané de lintégralité de la VM, contrairement aux instantanés LVM qui ne sauvegardent quun seul disque.</p>
<p>Pour créer un instantané libvirt, utilisez la commande <code class="language-plaintext highlighter-rouge">virsh snapshot-create</code><br />
Cette commande repose sur un fichier XML qui contient la définition de la VM originale. La commande virsh lit le contenu de ce fichier pour identifier la VM dorigine (telle que spécifiée dans la section <code class="language-plaintext highlighter-rouge">&lt;name&gt;</code> du fichier XML) et les disques à utiliser.</p>
<p>Le fichier XML peut être soit celui qui définit la VM originale, soit un fichier XML personnalisé ne contenant quune partie de la configuration de la VM. Vous pouvez ainsi créer un fichier XML qui ne contient que certains des disques utilisés par la VM. Utilisez par exemple la commande</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>virsh snapshot-create mondomaine-snap mondomaine.xml
</code></pre></div></div>
<p>pour créer un instantané appelé <strong>mondomaine-snap</strong> qui repose sur le fichier de configuration <strong>mondomaine.xml</strong>.</p>
<p>En termes de création dinstantanés KVM, le véritable avantage de la commande virsh tient au modificateur <code class="language-plaintext highlighter-rouge">--live</code><br />
Cette option vous permet de prendre un instantané dune VM en fonctionnement (« en ligne »).<br />
Lorsque vous lutilisez, testez systématiquement votre instantané ; en effet, certaines charges de travail nacceptent pas à chaud les instantanés.<br />
De même, ne perdez pas de vue quun instantané KVM sera plus volumineux car il intègre également limage de la mémoire.</p>
<p>Une fois linstantané KVM créé à laide de la commande virsh, vous pouvez revenir à celui-ci au moyen de la commande <code class="language-plaintext highlighter-rouge">snapshot-revert nom_vm</code></p>
<p>Par exemple, la commande <code class="language-plaintext highlighter-rouge">snapshot-revert mavm --current</code> rétablit le dernier instantané enregistré de la VM. Vous pouvez également spécifier le nom dun instantané particulier à rétablir au moyen dune commande telle que <code class="language-plaintext highlighter-rouge">virsh snapshot-revert mavm mavm-snapshot</code></p>
<p>Si la vieille méthode qui consiste à utiliser des volumes de stockage LVM fonctionne toujours, elle nen nécessite pas moins larrêt de la VM au moment de créer linstantané et ne tient pas non plus compte des métadonnées de cette VM. Cest pourquoi, même si LVM fonctionne encore dans les environnements KVM modernes, il est sans doute préférable de réaliser un instantané KVM avec la commande virsh de libvirt.</p>
<h2 id="gestion-machines-virtuelles-graphique">Gestion Machines Virtuelles (graphique)</h2>
<h3 id="vérifier-installation-qemukvm">Vérifier installation QEMU/KVM</h3>
<p>Ouvrez maintenant lapplication “virt-manager” à partir de votre menu dapplication.</p>
<p>Cliquez sur le menu <strong>“Edition -&gt; Détails de la connexion”</strong> sur lapplication virt-manager.<br />
Sur longlet <strong>“Affichage”</strong> vous verrez que le virt-manager se connectera automatiquement à <code class="language-plaintext highlighter-rouge">"qemu:///system"</code><br />
<img src="/images/qemu-vmm01.png" alt="" /><br />
virt-manager se connectera automatiquement à QEMU/KVM dans le système.</p>
<p>Allez dans longlet <strong>“Réseaux virtuels”</strong> et vous verrez la configuration réseau “par défaut”<br />
<img src="/images/qemu-vmm02.png" alt="" /></p>
<p>Préférences , aller dans <strong>“Edition -&gt; Preferences”</strong> et activer <strong>Enable xml editing</strong><br />
<img src="/images/qemu-vmm02a.png" alt="" /></p>
<h3 id="gestion-réseau">Gestion réseau</h3>
<p><strong>Créer et configurer une passerelle réseau pour KVM</strong><br />
Le pont Linux, lorsquil est utilisé dans KVM, permet à une machine virtuelle daccéder à un réseau et à des services externes en dehors de lenvironnement virtuel.</p>
<p>Il existe différentes façons de configurer le Bridge Networking sous Linux pour une utilisation en KVM. Le réseau par défaut utilisé par une machine virtuelle lancée dans KVM est le réseau NAT. Avec le réseau NAT, un réseau virtuel est créé pour les machines invitées qui est ensuite mis en correspondance avec le réseau hôte pour fournir une connectivité internet.</p>
<p>Lorsque vous configurez et utilisez la mise en réseau pontée, les systèmes dexploitation invités accèdent à un réseau externe connecté directement à la machine hôte. Un pont peut être créé soit à laide du <strong>gestionnaire de machines virtuelles</strong>, soit à laide de loutil de ligne de commande <strong>virsh</strong>, soit en éditant directement des scripts réseau, soit en utilisant les outils de gestion de réseau Linux.</p>
<h3 id="création-passerelle-réseau">Création passerelle réseau</h3>
<p>Ouvrez le <strong>Gestionnaire de machines virtuelles</strong>, puis allez dans <br />
Édition → Détails de la connexion → Réseaux virtuels</p>
<p>Configurez une nouvelle interface réseau en cliquant sur le + en bas de la fenêtre. Donnez un nom au réseau virtuel.<br />
<img src="/images/bridge001.png" alt="" width="300" /></p>
<p>Cliquez sur le bouton “Forward”, dans la fenêtre suivante, fournissez des informations sur le réseau virtuel.<br />
<img src="/images/bridge002.png" alt="" width="300" /></p>
<p>Cliquez sur “Forward” et choisissez si vous souhaitez activer lIPv6.<br />
<img src="/images/bridge003.png" alt="" width="300" /></p>
<p>Sélectionnez le type de réseau et la politique de transfert.<br />
<img src="/images/bridge004.png" alt="" width="300" /></p>
<p>Terminez le paramétrage et enregistrez vos configurations. Le nouveau réseau virtuel devrait safficher sur la page daperçu.<br />
<img src="/images/bridge005.png" alt="" width="300" /></p>
<p>Un pont sur le système hôte est automatiquement créé pour le réseau.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brctl show virbr4
</code></pre></div></div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bridge name bridge id STP enabled interfaces
virbr4 8000.525400c2410a yes virbr4-nic
</code></pre></div></div>
<h3 id="créer-et-démarrer-une-vm">Créer et démarrer une VM</h3>
<p>Démarrer lapplication graphique <strong>Gestionnaire de machines virtuelles</strong> <br />
Il faut au préalable vérifier si le réseau est actif<br />
Edition → Détails de la connexion : Réseaux Virtuels <br />
<img src="/images/kvm0.png" alt="kvm" width="300" /></p>
<p>Si la fenêtre est vide<br />
<img src="/images/kvm1-b.png" alt="kvm" /> <br />
Fichier → +Ajouter une connexion<br />
<img src="/images/kvm1-a.png" alt="kvm" /> <br />
<img src="/images/kvm1.png" alt="kvm" /></p>
<p>Le bouton <em>Nouveau</em> permet de lancer lassistant de création<br />
<img src="/images/kvm2.png" alt="kvm" /></p>
<p><img src="/images/kvm3.png" alt="kvm" /></p>
<p><img src="/images/kvm4.png" alt="kvm" /></p>
<p><img src="/images/kvm5.png" alt="kvm" /></p>
<p>Machine virtuelle <strong>/home/yannick/virtuel/KVM/debian10.qcow2</strong></p>
<p><img src="/images/kvm6-1.png" alt="kvm" /></p>
<p><img src="/images/kvm8.png" alt="kvm" /></p>
<p><img src="/images/kvm9.png" alt="kvm" /></p>
<p>Dans les utilisations suivantes, si rien nest activé automatiquement, il faut exécuter les commandes suivantes</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> <span class="nt">-s</span>
<span class="c"># translation d'adresse (NAT) pour permettre au VM de sortir sur Internet</span>
<span class="nb">echo </span>1 <span class="o">&gt;</span> /proc/sys/net/ipv4/ip_forward
<span class="c"># lancer le service libvirtd pour l'application graphique</span>
systemctl start libvirtd
</code></pre></div></div>
<p>Démarrer lapplication graphique <strong>Gestionnaire de machines virtuelles</strong></p>
<p><img src="/images/kvm10.png" alt="kvm" /></p>
<p>Le démarrage de la machine virtuelle provoque une erreur (réseau non actif)</p>
<p><img src="/images/kvm11.png" alt="kvm" /></p>
<p>Cliquer sur <em>Edition</em> puis <em>Détails de la connexion</em></p>
<p><img src="/images/kvm12.png" alt="kvm" /></p>
<p>Cliquer sur licône <em>Démarrer le réseau</em> pour lactiver</p>
<p><img src="/images/kvm13.png" alt="kvm" /></p>
<p>Pour modifier les paramètres de la machine virtuelle, cliquer sur <em>Edition</em> puis <em>Détails de la machine virtuelle</em></p>
<p><img src="/images/kvm15.png" alt="kvm" /></p>
<p>Cliquer sur licône <em>Démarrer la machine virtuelle</em> de la fenêtre <strong>“Gestionnaire de machines virtuelles”</strong></p>
<p><img src="/images/kvm14.png" alt="kvm" /></p>
<p>Puis cliquer sur <em>Afficher</em> et sélectionner <em>Détails</em><br />
Après une installation utilisant le CD et un fichier ISO, il faut le déconnecter<br />
Sélectionner <strong>IDE CD-ROM</strong> puis clique sur <strong>Déconnecter</strong></p>
<p><img src="/images/kvm16.png" alt="kvm" /></p>
<p>Cliquer sur licône <em>Démarrer la machine virtuelle</em> puis sur licône <em>Afficher la console graphique</em></p>
<p><img src="/images/kvm17.png" alt="kvm" /></p>
<h2 id="partage-de-fichiers">Partage de fichiers</h2>
<p><em>entre lhôte et les invités dans qemu/kvm</em></p>
<p><img src="/images/partage-9p.png" alt="image" width="200px" /><br />
<a href="https://www.lafilacroche.com/post/partage-9p-entre-hote-et-invite-avec-virt-manager-et-debian">9p, lautre pays du partage</a></p>
<h3 id="hôte">Hôte</h3>
<p>Créer un dossier de partage sur lhôte.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir $HOME/qemu-share
</code></pre></div></div>
<p>Les droits en lecture/écriture pour le propriétaire et le groupe du dossier</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod 775 -R $HOME/qemu-share
</code></pre></div></div>
<p>Dans le fichier <strong>/etc/libvirt/qemu.conf</strong>, chercher les lignes suivantes :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#user = "root"
#group = "root"
</code></pre></div></div>
<p>Et remplacer par :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>user = "utilisateur"
group = "groupe"
</code></pre></div></div>
<p><strong>utilisateur</strong> et <strong>groupe</strong> sont remplacés par ceux du dossier que lon veut partager sur la machine hôte et supprimer le # en début de ligne.</p>
<p>Redémarrer le service libvirtd pour que ces modifications soient prises en compte:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>systemctl restart libvirtd
</code></pre></div></div>
<p><em>Cela va permettre à la machine virtuelle décrire dans le dossier partagé avec lidentité de notre utilisateur plutôt quavec lidentité Libvirt Qemu paramétrée par défaut. Ainsi notre utilisateur aura accès aux fichiers créés par le serveur situé sur la VM et pourra les modifier sans souci.</em></p>
<p>Dans <strong>virt-manager</strong>, <strong>“Edition” -&gt; “Détails de la machine virtuelle”</strong> et dans les informations de la machine (icône “Afficher les détails du matériel virtuel”) ,cliquer sur “Ajouter un matériel”, puis sur “Système de fichiers”.Modifier suivant le modèle ci-dessous.</p>
<p><img src="/images/qemu-share.png" alt="qemu share" /></p>
<p>Pilote = <strong>default</strong><br />
Mode = <strong>Squash</strong><br />
Chemin source : il sagit du dossier crée précédemment sur lhôte <strong>/home/yannick/qemu-share</strong><br />
Chemin cible : un nom au choix, par exemple : <strong>hotshare</strong></p>
<h3 id="invité-vm">Invité (VM)</h3>
<p>Démarrer la VM et ouvrir un terminal.<br />
Créer le répertoire de montage du système de fichiers</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir $HOME/share
</code></pre></div></div>
<p>Montage manuel du système de fichiers.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mount -t 9p -o trans=virtio,version=9p2000.L,rw hostshare $HOME/share
</code></pre></div></div>
<p>Montage automatique au démarrage ajout de la ligne suivante au fichier <strong>/etc/fstab</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hostshare /home/utilisateur/share 9p rw,relatime,sync,dirsync,trans=virtio,version=9p2000.L 0 2
</code></pre></div></div>
<p>Montage</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo mount -a
</code></pre></div></div>
<blockquote>
<p><em>Le partage ne peut pas être monté et édité sur plusieurs hôtes en même temps. Assurez-vous donc de le démonter avant de le monter sur un autre hôte invité.</em></p>
</blockquote>
<h2 id="annexe">Annexe</h2>
<h3 id="arrêt-vm-avant-extinction-hôte">Arrêt VM avant extinction hôte</h3>
<p><em>paramètres du service libvirt-guests pour permettre larrêt gracieux des invités</em></p>
<ul>
<li><a href="/2022/09/03/Qemu_KVM-libvirt-guests-arret-VM-en-douceur.html">Arrêt en douceur des machines virtuelles lorsque la machine hôte est bloquée, mise hors tension ou redémarrée</a></li>
</ul>
<h3 id="vmm---plein-écran-ou-pas">VMM - Plein écran ou pas</h3>
<p>Pour quitter le mode fullscreen de virt-manager, il suffit de déplacer la souris en haut de lécran et au centre. Une barre blanche permet de faire apparaître deux icônes.</p>
<p><img src="/images/vmm-0002.png" alt="" /></p>
<p><img src="/images/vmm-0003.png" alt="" /></p>
<h3 id="cas-des-images-créées-via-qemu">Cas des images créées via qemu</h3>
<h4 id="créer-image-via-qemu">Créer image via qemu</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-img create -f qcow2 /var/lib/libvirt/images/DOMAIN.img 20G
</code></pre></div></div>
<ul>
<li><strong>-f</strong> → format de KVM pour le fichier image, qcow2 est le format kvm par défaut, raw est compatible avec Virtualbox et VMware</li>
<li><strong>20G</strong> → Remplacer par la valeur souhaitée, ce sera la taille du disque dur virtuel</li>
</ul>
<h4 id="booter-sur-une-iso-et-installer-los-via-kvm-dans-limage-précédemment-créée">Booter sur une ISO et installer lOS via KVM dans limage précédemment créée</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kvm -m 1024 -cdrom /CHEMIN_VERS_VOTRE_ISO -boot d /var/lib/libvirt/images/DOMAINE.img
</code></pre></div></div>
<ul>
<li><strong>-m 1024</strong> → La quantité de ram allouée</li>
<li><strong>-cdrom</strong> → spécifie le chemin vers votre ISO</li>
<li><strong>-boot d</strong> → spécifie sur quelle domaine booter, un système émulé sappelle domaine, remplacer DOMAINE par un titre parlant par exemple DebianVM</li>
</ul>
<h4 id="booter-simplement-sur-los-fraîchement-installé-directement-via-kvm">Booter simplement sur lOS fraîchement installé directement via KVM</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kvm -boot -d /var/lib/libvirt/images/DOMAINE.img
</code></pre></div></div>
<blockquote>
<p><strong>Attention</strong> en utilisant KVM directement et non avec <strong>libvirt</strong>, <u>les machines virtuelles ne seront pas listés avec virtmanager ou la commande</u> : <code class="language-plaintext highlighter-rouge">virsh list</code></p>
</blockquote>
<h4 id="import-machine-créée-via-qemu-pour-gestion-avec-virt-manager--libvirt">Import machine créée via “qemu” pour gestion avec “virt-manager” libvirt</h4>
<p>Si vous avez déjà créé précédemment une machine virtuelle avec <strong>qemu</strong> vous pouvez importer cette machine virtuelle pour quelle soit gérée via <strong>virt-manager</strong> et par conséquent quelle utilise <strong>libvirt</strong>.</p>
<p>Par exemple, si vous avez une image qui se trouve dans <strong>/srv/vms/Fedora12.img</strong>, effectuez ces opérations:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virt-install --import --disk path=/srv/vms/Fedora12.img --os-type linux --os-variant fedora11 --ram 512 --name Fedora12
</code></pre></div></div>
<p>Si vous désirez utiliser la gestion de laccélération (cest à dire, de passer par kvm et non pas qemu seulement):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virt-install --import --accelerate --disk path=/srv/vms/Fedora12.img --os-type linux --os-variant fedora11 --ram 512 --name Fedora12
</code></pre></div></div>
<ul>
<li>Les options <em>name</em>, <em>ram</em> sont obligatoires.</li>
<li>Les options <em>os-type</em> et <em>os-variant</em> ne sont pas obligatoires mais permettent tout de même une meilleure gestion pour le démarrage et mémoire au boot.</li>
</ul>
<p>Pour les machines virtuelles Windows, cest toujours aussi simple:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo virt-install --import --accelerate --disk path=/srv/vms/WinXP.img --os-type windows --os-variant winxp --ram 512 --name WindowXP
</code></pre></div></div>
<p>Aussitôt la génération de la configuration effectuée, la machine va démarrer. Elle apparait alors dans <strong>virt-manager</strong>.</p>
<h2 id="lxc">LXC</h2>
<h3 id="conteneurs-privilégiés-ou-non-privilégiés">Conteneurs privilégiés ou non privilégiés</h3>
<p><em>Les LXC peuvent être configurés pour fonctionner dans des configurations privilégiées ou non privilégiées.</em></p>
<p>En général, l<u>exécution d'un conteneur non privilégié est considérée comme plus sûre</u> que lexécution dun conteneur privilégié, car les conteneurs non privilégiés ont un degré disolation accru en vertu de leur conception. Lélément clé est le mappage de lUID racine dans le conteneur à un UID non racine sur lhôte, ce qui rend plus difficile pour un piratage à lintérieur du conteneur dentraîner des conséquences sur le système hôte. En dautres termes, si un attaquant parvient à séchapper du conteneur, il devrait se retrouver avec des droits limités ou nuls sur lhôte.</p>
<p>Les paquets du noyau Arch linux, <strong>linux-lts</strong> et <strong>linux-zen</strong> fournissent actuellement un support prêt à lemploi pour les conteneurs non privilégiés. De même, avec le paquetage <strong>linux-hardened</strong>, les conteneurs non privilégiés ne sont disponibles que pour ladministrateur système ; avec des modifications supplémentaires de la configuration du noyau nécessaires, car les espaces de noms dutilisateurs y sont désactivés par défaut pour les utilisateurs normaux.</p>
<p>Cet article contient des informations permettant aux utilisateurs dexécuter lun ou lautre type de conteneur, mais des étapes supplémentaires peuvent être nécessaires pour utiliser les conteneurs non privilégiés.</p>
<p>Un exemple pour illustrer les conteneurs non privilégiés</p>
<p>Pour illustrer la puissance du mappage dUID, considérez la sortie ci-dessous dun conteneur non privilégié en cours dexécution. Nous y voyons les processus conteneurisés appartenant à lutilisateur root du conteneur dans la sortie de ps :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[root@unprivileged_container /]# ps -ef | head -n 5
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 17:49 ? 00:00:00 /sbin/init
root 14 1 0 17:49 ? 00:00:00 /usr/lib/systemd/systemd-journald
dbus 25 1 0 17:49 ? 00:00:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
systemd+ 26 1 0 17:49 ? 00:00:00 /usr/lib/systemd/systemd-networkd```
Sur l'hôte, cependant, ces processus racine conteneurisés sont en fait exécutés sous l'utilisateur mappé (ID&gt;100000), plutôt que sous l'utilisateur **root** réel de l'hôte :
</code></pre></div></div>
<p>[root@host /]# lxc-info -Ssip name sandbox
State: RUNNING
PID: 26204
CPU use: 10.51 seconds
BlkIO use: 244.00 KiB
Memory use: 13.09 MiB
KMem use: 7.21 MiB</p>
<p>[root@host /]# ps -ef | grep 26204 | head -n 5
UID PID PPID C STIME TTY TIME CMD
100000 26204 26200 0 12:49 ? 00:00:00 /sbin/init
100000 26256 26204 0 12:49 ? 00:00:00 /usr/lib/systemd/systemd-journald
100081 26282 26204 0 12:49 ? 00:00:00 /usr/bin/dbus-daemon system address=systemd: nofork nopidfile systemd-activation
100000 26284 26204 0 12:49 ? 00:00:00 /usr/lib/systemd/systemd-logind</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
### Activer le support pour exécuter des conteneurs non privilégiés (facultatif)
Modifiez `/etc/lxc/default.conf` pour qu'il contienne les lignes suivantes :
</code></pre></div></div>
<p>lxc.idmap = u 0 100000 65536
lxc.idmap = g 0 100000 65536</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
En d'autres termes, mappez une plage de 65536 uids consécutifs, en partant de l'uid 0 côté conteneur, qui sera l'uid 100000 du point de vue de l'hôte, jusqu'à l'uid 65535 côté conteneur inclus, que l'hôte connaîtra comme l'uid 165535. Appliquez ce même mappage aux gids.
Créez `/etc/subuid` et `/etc/subgid` pour contenir le mappage des paires uid/gid conteneurisées pour chaque utilisateur qui pourra exécuter les conteneurs.
L'exemple ci-dessous est simplement pour les utilisateurs yann root (et l'unité système systemd) :
/etc/subuid
</code></pre></div></div>
<p>yann:100000:65536
root:100000:65536</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
/etc/subgid
</code></pre></div></div>
<p>yann:100000:65536
root:100000:65536</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
En outre, l'exécution de conteneurs non privilégiés en tant qu'utilisateur non privilégié ne fonctionne que si vous déléguez un cgroup à l'avance (le modèle de délégation cgroup2 applique cette restriction, pas liblxc). Utilisez la commande systemd suivante pour déléguer le cgroup (selon [LXC - Getting started : Creating unprivileged containers as a user](https://linuxcontainers.org/lxc/getting-started/#creating-unprivileged-containers-as-a-user)) :
systemd-run --unit=myshell --user --scope -p "Delegate=yes" lxc-start nom_du_conteneur
Cela fonctionne de la même manière pour les autres commandes lxc.
Alternativement, déléguer les cgroups non privilégiés en créant une unité systemd (par Rootless Containers : Enabling CPU, CPUSET, and I/O delegation) :
/etc/systemd/system/user\@1000.service.d/delegate.conf
</code></pre></div></div>
<p>[Service]
Delegate=cpu cpuset io memory pids
```</p>
<p><a href="https://discuss.linuxcontainers.org/t/lxc-on-arch-linux-error-main-260-no-container-config-specified/13252">Lxc on Arch linux - error main: 260 No container config specified</a></p>
<hr />
<p>Toujours dans ce même fichier de configuration, vous devrez remplacer votre fichier existant (ou similaire) :</p>
<p>lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536</p>
<p>Par quelque chose comme (ceci suppose que luid/gid de votre utilisateur est 1000/1000) :</p>
<p>lxc.id_map = u 0 100000 1000
lxc.id_map = g 0 100000 1000
lxc.id_map = u 1000 1000 1
lxc.id_map = g 1000 1000 1
lxc.id_map = u 1001 101001 64535
lxc.id_map = g 1001 101001 64535</p>
<p>Ces mappages signifient donc que votre conteneur a 65536 uids et gids mappés, de 0 à 65535 dans le conteneur. Ceux-ci sont mappés aux identifiants dhôtes 100000 à 165535 avec une exception, luid et le gid 1000 ne sont pas traduits. Cette astuce est nécessaire pour que votre utilisateur dans le conteneur puisse accéder à la socket X, à la socket pulseaudio et aux périphériques DRI/snd comme votre propre utilisateur le peut (cela nous évite beaucoup de configuration sur lhôte).</p>
<p>LXC containers started by non-root</p>
<p>Assume that <a href="https://wiki.debian.org/LXC#Unprivileged_container">preparation of unprivileged containers</a> has been done. LXC needs a CGroup directory that can be manipulated by LXC, which was traditionally prepared by libpam-cgfs. libpam-cgfs no longer works and becomes unnecessary in the unified hierarchy as 946170.</p>
<p>When lxc-start or lxc-execute is run for lxc (1:4.0.6-1), we can use them, for example, as</p>
<p>systemd-run user scope -p “Delegate=yes” lxc-start -n container-name</p>
<p>Previous versions of LXC required as</p>
<p>systemd-run user scope -p “Delegate=yes” lxc-start -F -n container-name</p>
<p>otherwise we get error message reported at <a href="https://github.com/lxc/lxc/issues/3221">https://github.com/lxc/lxc/issues/3221</a>. The above call does not work reliably if you want to start the container in the background. You cannot simply omit the -F parameter. Start the container in the background with</p>
<p>systemd-run user -r -p “Delegate=yes” lxc-start -F -n container-name</p>
<p>See: https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1716279.html</p>
<h2 id="liens">Liens</h2>
<ul>
<li>Red Hat
<ul>
<li><a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html-single/virtualization_administration_guide/index#masthead">Virtualization Administration Guide Red Hat</a></li>
<li><a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_atomic_host/7/html-single/getting_started_with_containers/index">Getting Started with Containers</a></li>
</ul>
</li>
<li><a href="https://wiki.evolix.org/HowtoKVM">HowTo KVM</a></li>
<li><a href="https://guide.ubuntu-fr.org/server/libvirt.html">libvirt doc ubuntu</a></li>
<li><a href="https://zestedesavoir.com/billets/2403/installations-automatisees-de-machines-virtuelles-avec-libvirt/">Installations automatisées de machines virtuelles avec libvirt</a></li>
<li><a href="https://www.cyberciti.biz/faq/how-to-add-network-bridge-with-nmcli-networkmanager-on-linux/">How to add network bridge with nmcli (NetworkManager) on Linux</a></li>
<li><a href="https://www.zenzla.com/linux/1462-la-virtualisation-avec-kvm-libvirt-et-virt-manager.html">La virtualisation avec KVM, libvirt et virt-manager</a></li>
</ul>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2022-10-25T00: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="/2022/10/22/Nginx_headers_SSL_HSTS_OCSP.html">Nginx headers,SSL,HSTS,OCSP</a></div><div class="next"><span>SUIVANT</span><a href="/2022/10/26/EndeavourOS-Chiffrement-LUKS-LVM.html">TEST VM EndeavourOS avec chiffrement complet du disque LVM sur LUKS</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>