3354 lines
268 KiB
HTML
3354 lines
268 KiB
HTML
|
<!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} {title}</a></li>'
|
|||
|
searchResultTemplate: '<li><a href="{url}">{date} {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>
|
|||
|
<a class="button button--secondary button--pill button--sm"
|
|||
|
href="/archive.html?tag=virtuel">virtuel</a>
|
|||
|
</li><li>
|
|||
|
<a class="button button--secondary button--pill button--sm"
|
|||
|
href="/archive.html?tag=network">network</a>
|
|||
|
</li></ul><ul class="right-col menu"><li>
|
|||
|
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF">25 oct. 2022</span>
|
|||
|
|
|||
|
<span title="Modification" style="color:#00FF7F">1r mars 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">⇧</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 l’accélération matérielle lorsqu’elle 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é d’un processeur Intel)</p>
|
|||
|
|
|||
|
<p>Une différence entre les deux est que la <strong>QEMU</strong> fonctionne sur un processeur sans avoir besoin d’une extension de virtualisation matérielle (Intel VT/VT-d, AMD-V) alors que la <strong>KVM</strong> l’utilise. Les extensions de virtualisation matérielle vous permettent d’accéder directement au matériel sur la machine physique. L’inconvé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 d’exé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 l’on appelle des hyperviseurs.<br />
|
|||
|
<strong>Il existe plusieurs types d’hyperviseurs, 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 s’effectue grâce à un logiciel installé sur un système d’exploitation. Donc la machine virtualisée n’interagit 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 d’autres.</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, c’est un client</strong>.<br />
|
|||
|
Et la liaison entre Qemu et KVM est faite via l’API libvirt ( management du réseau, stockages, clavier, souris, etc )</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p><a href="http://libvirt.org/">libvirt</a> est une bibliothèque permettant d’interagir avec différentes solutions de virtualisation (cet article s’intéressera uniquement à KVM/QEMU, mais Xen, VirtualBox et d’autres sont aussi possibles)<br />
|
|||
|
<a href="http://virt-manager.org/">Virtual Machine Manager</a> est un ensemble d’applications 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 l’image d’une machine</li>
|
|||
|
<li><strong>virt-image</strong> permet de créer un nouvelle machine à partir d’une image</li>
|
|||
|
<li><strong>virt-install</strong> permet de créer une nouvelle machine ou d’importer 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 d’optimiser l’émulation lorsque qu’elle concerne la même architecture).QEMU peut utiliser KVM lors de l’exécution d’une architecture cible identique à l’architecture hôte. Par exemple, lorsque vous exécutez qemu-system-x86 sur un processeur compatible x86, vous pouvez profiter de l’accé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 à d’autres programmes de virtualisation tels que VirtualBox et VMware, QEMU ne fournit pas d’interface graphique pour gérer les machines virtuelles (autre que la fenêtre qui apparaît lors de l’exécution d’une 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 l’hô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 à l’aide 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 n’est affiché après l’exé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 l’activation 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 l’un 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 n’est disponible que s’il 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 d’utiliser des appareils sur la machine hôte. KVM fournit des périphériques para-virtualisés aux machines virtuelles en utilisant l’API Virtio comme couche entre l’hyperviseur et l’invité.</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 l’accé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. L’interface Virt-manager permet à l’utilisateur 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 d’autres 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 d’exploitation</li>
|
|||
|
<li><strong>virt-viewer</strong> : L’interface 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 d’architecture x86 qui possèdent l’extension 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 s’agit d’un émulateur gratuit à code source ouvert qui peut effectuer une virtualisation matérielle. Il émule le processeur de la machine hôte par le biais d’une 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 d’exécuter une variété de systèmes invités.</p>
|
|||
|
|
|||
|
<p><strong>KVM</strong> peut être utilisé avec <strong>QEMU</strong>, ce qui permet d’exé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 l’interface nft). <br />
|
|||
|
<strong>bridge-utils</strong> : Utilitaires de pont Ethernet.<br />
|
|||
|
<strong>dmidecode</strong> pour éviter l’erreur<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 à l’installation ? [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 d’outils utilisés pour accéder aux images disques des machines virtuelles (VM) et les modifier. Vous pouvez l’utiliser pour :</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>visualiser et modifier des fichiers à l’inté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 l’autoriser.<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 l’adhé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 l’adhé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 l’utilisateur à <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 d’accé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 d’utilisateur 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 d’un 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 d’accès à un répertoire de système de fichiers pour le stockage des fichiers d’images d’invités. Si ce répertoire n’existe 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 l’objet 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 qu’il 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 d’exécution. Si vous voulez que le pool soit accessible même si la machine virtuelle invitée n’est pas en cours d’exé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 d’exé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 d’un 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 l’on utilisera la translation d’adresse (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">></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 n’existe 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 l’interface 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 l’ordinateur 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 d’une 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 d’exé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 && 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 d’un pont réseau en utilisant l’outil hérité <code class="language-plaintext highlighter-rouge">brctl</code> du paquetage <strong>bridge-utils</strong>.</p>
|
|||
|
|
|||
|
<p class="warning">L’utilisation 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">L’ajout d’une interface à un pont fera perdre à l’interface son adresse IP existante. Si vous êtes connecté à distance via l’interface que vous avez l’intention d’ajouter 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 d’abord 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é l’adaptateur 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 n’importe quelle combinaison de périphériques réseau en éditant l’option <code class="language-plaintext highlighter-rouge">BindsToInterfaces</code>.<br />
|
|||
|
Si l’un 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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> 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: <BROADCAST,MULTICAST> 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: <POINTOPOINT,NOARP,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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>J’ai une «Connexion filaire 1» qui utilise l’interface Ethernet <strong>enp0s31f6</strong>. Mon système dispose également d’une 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 l’interface 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: <LOOPBACK,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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: <POINTOPOINT,NOARP,UP,LOWER_UP> 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: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 d’un pont réseau à l’aide de l’outil 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>L’ajout de l’interface 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 l’utilitaire bridge (qui fait également partie d’iproute2).</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 d’un 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>L’interface 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 d’un 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: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br1 state forwarding priority 32 cost 5
|
|||
|
3: enp0s31f6: <BROADCAST,MULTICAST,UP,LOWER_UP> 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"><network></span>
|
|||
|
<span class="nt"><name></span>host-bridge<span class="nt"></name></span>
|
|||
|
<span class="nt"><forward</span> <span class="na">mode=</span><span class="s">"bridge"</span><span class="nt">/></span>
|
|||
|
<span class="nt"><bridge</span> <span class="na">name=</span><span class="s">"br0"</span><span class="nt">/></span>
|
|||
|
<span class="nt"></network></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"># -> Réseau host-bridge défini depuis host-bridge.xml</span>
|
|||
|
virsh net-start host-bridge <span class="c"># -> Réseau host-bridge démarré</span>
|
|||
|
virsh net-autostart host-bridge <span class="c"># -> 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 d’avoir 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 l’hôte à l’invité.</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> : L’emplacement pour sauvegarder le hdd et la taille de la machine virtuelle. Dans notre exemple, j’ai alloué une taille de hdd de 8 Go.</li>
|
|||
|
<li><strong>-cdrom</strong> : L’emplacement de l’image ISO de l’installateur. Veuillez noter que vous devez avoir l’image ISO actuelle à cet endroit.</li>
|
|||
|
<li><strong>-graphics vnc</strong> : Permet à la VNC d’accé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 n’est apparu parce que vous avez lancé virt-install sur un terminal qui n’avait pas d’information d’affichage X disponible, donc il ne pouvait pas démarrer virt-viewer pour afficher la console de la machine virtuelle.<br />
|
|||
|
Éventuellement, l’installation 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 l’attente de l’installation virt-install. Comme la VM est toujours en cours d’exécution, l’installation 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 l’installation, si virt-manager a la permission de gérer l’hyperviseur 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 l’interface graphique du gestionnaire de virt (vmm, virt-manager) pour démarrer au démarrage de l’hô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 l’hô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 > /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 d’info 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 d’un 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 d’un 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 l’interface graphique du gestionnaire de virt n’a 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 l’exemple ci-dessus, nous nous connectons en tant que’myuser’ 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 fichier’id_rsa’.</p>
|
|||
|
|
|||
|
<p>virt-manager devrait vous demander immédiatement la phrase de chiffrement protégeant la clé privée (ce n’est pas le mot de passe de l’utilisateur !), et une fois que vous l’aurez entré, vous verrez virt-manager comme si vous étiez assis sur l’hô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 d’utilisateur 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 d’un 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"><graphics type='vnc' port='5900' autoport='yes' listen='127.0.0.1'></code></p>
|
|||
|
|
|||
|
<blockquote>
|
|||
|
<p>Notez le numéro de port 5900. Installez n’importe quelle application client VNC.</p>
|
|||
|
</blockquote>
|
|||
|
|
|||
|
<p>Pour ce guide, j’utiliserai TigerVnc. TigerVNC est disponible dans les dépôts par défaut d’Arch Linux. Pour l’installer 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 l’application 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 l’adresse 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 l’utilitaire <strong>virt-viewer</strong> pour installer le système d’exploitation 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 l’accè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 d’une version antérieure de l’état des fichiers sur un volume logique. Les volumes LVM sont créés à partir d’un groupe de volumes qui représente la quantité totale d’espace disponible.<br />
|
|||
|
Avant de créer un instantané LVM, vous devez vous assurer qu’un espace disque suffisant est disponible dans le groupe de volumes.<br />
|
|||
|
Pour créer un instantané, la procédure est simple ; il suffit d’exé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 l’interface d’administration de KVM. Créer des instantanés avec libvirt constitue une meilleure approche car il s’agit d’une solution native.<br />
|
|||
|
Libvirt prend un instantané de l’intégralité de la VM, contrairement aux instantanés LVM qui ne sauvegardent qu’un 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 d’origine (telle que spécifiée dans la section <code class="language-plaintext highlighter-rouge"><name></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 qu’une 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 d’instantané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é d’une VM en fonctionnement (« en ligne »).<br />
|
|||
|
Lorsque vous l’utilisez, testez systématiquement votre instantané ; en effet, certaines charges de travail n’acceptent pas à chaud les instantanés.<br />
|
|||
|
De même, ne perdez pas de vue qu’un instantané KVM sera plus volumineux car il intègre également l’image de la mémoire.</p>
|
|||
|
|
|||
|
<p>Une fois l’instantané KVM créé à l’aide 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 d’un instantané particulier à rétablir au moyen d’une 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 n’en nécessite pas moins l’arrêt de la VM au moment de créer l’instantané et ne tient pas non plus compte des métadonnées de cette VM. C’est 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 l’application “virt-manager” à partir de votre menu d’application.</p>
|
|||
|
|
|||
|
<p>Cliquez sur le menu <strong>“Edition -> Détails de la connexion”</strong> sur l’application virt-manager.<br />
|
|||
|
Sur l’onglet <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 l’onglet <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 -> 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, lorsqu’il est utilisé dans KVM, permet à une machine virtuelle d’accéder à un réseau et à des services externes en dehors de l’environnement 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 d’exploitation invités accèdent à un réseau externe connecté directement à la machine hôte. Un pont peut être créé soit à l’aide du <strong>gestionnaire de machines virtuelles</strong>, soit à l’aide de l’outil 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 l’IPv6.<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 s’afficher sur la page d’aperç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 l’application 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 l’assistant 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 n’est 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">></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 l’application 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 l’icône <em>Démarrer le réseau</em> pour l’activer</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 l’icô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 l’icône <em>Démarrer la machine virtuelle</em> puis sur l’icô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 l’hô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, l’autre pays du partage</a></p>
|
|||
|
|
|||
|
<h3 id="hôte">Hôte</h3>
|
|||
|
|
|||
|
<p>Créer un dossier de partage sur l’hô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 l’on 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 l’identité de notre utilisateur plutôt qu’avec l’identité 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” -> “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 s’agit du dossier crée précédemment sur l’hô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 l’arrê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 l’OS via KVM dans l’image 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é s’appelle 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 l’OS 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 qu’elle soit gérée via <strong>virt-manager</strong> et par conséquent qu’elle 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 l’accélération (c’est à 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, c’est 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 l’exécution d’un conteneur privilégié, car les conteneurs non privilégiés ont un degré d’isolation accru en vertu de leur conception. L’élément clé est le mappage de l’UID racine dans le conteneur à un UID non racine sur l’hôte, ce qui rend plus difficile pour un piratage à l’intérieur du conteneur d’entraîner des conséquences sur le système hôte. En d’autres termes, si un attaquant parvient à s’échapper du conteneur, il devrait se retrouver avec des droits limités ou nuls sur l’hôte.</p>
|
|||
|
|
|||
|
<p>Les paquets du noyau Arch linux, <strong>linux-lts</strong> et <strong>linux-zen</strong> fournissent actuellement un support prêt à l’emploi 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 l’administrateur système ; avec des modifications supplémentaires de la configuration du noyau nécessaires, car les espaces de noms d’utilisateurs y sont désactivés par défaut pour les utilisateurs normaux.</p>
|
|||
|
|
|||
|
<p>Cet article contient des informations permettant aux utilisateurs d’exécuter l’un ou l’autre 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 d’UID, considérez la sortie ci-dessous d’un conteneur non privilégié en cours d’exécution. Nous y voyons les processus conteneurisés appartenant à l’utilisateur 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>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 l’uid/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 d’hôtes 100000 à 165535 avec une exception, l’uid 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 l’hô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>
|
|||
|
|
|||
|
 </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} {title}</a> (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>
|
|||
|
|