yannstatic/static/2020/10/03/Virtualisation-Linux-(chroot,lxc,docker,kvm-qemu),libvirt-et-virsh.html

2196 lines
216 KiB
HTML
Raw Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>Virtualisation Linux (chroot,lxc,docker,kvm qemu),libvirt et virsh - YannStatic</title>
<meta name="description" content="Virtualisation Linux">
<link rel="canonical" href="https://static.rnmkcy.eu/2020/10/03/Virtualisation-Linux-(chroot,lxc,docker,kvm-qemu),libvirt-et-virsh.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root">
<!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner">
<div class="page__header d-print-none">
<header class="header"><div class="main">
<div class="header__title">
<div class="header__brand">
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewbox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.516 C 137.230 220.679,138.000 221.92
" href="/">YannStatic</a>
</div>
<!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text">
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche -->
</div>
<nav class="navigation">
<ul>
<li class="navigation__item"><a href="/archive.html">Etiquettes</a></li>
<li class="navigation__item"><a href="/htmldoc.html">Documents</a></li>
<li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li>
2024-11-28 11:42:23 +01:00
<li class="navigation__item"><a href="/syntaxe-markdown.html">Aide</a></li>
2024-10-31 20:18:37 +01:00
</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;">Virtualisation Linux (chroot,lxc,docker,kvm qemu),libvirt et virsh</h1></header></div>
<meta itemprop="headline" content="Virtualisation Linux (chroot,lxc,docker,kvm qemu),libvirt et virsh">
<div class="article__info clearfix">
<ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=virtuel">virtuel</a>
2024-10-31 20:18:37 +01:00
</li></ul>
<ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i> <span title="Création" style="color:#FF00FF"> 3 oct.  2020</span>
</li></ul>
</div>
<meta itemprop="datePublished" content="2020-10-03T00:00:00+02:00">
<meta itemprop="keywords" content="virtuel">
<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><h1 id="virtualisation-linux">Virtualisation Linux</h1>
<p>Regardez le mélange des environnements de systèmes dexploitation visible à travers le bureau unique illustré ci-dessous. Il sagit dun système Red Hat Enterprise Linux fonctionnant en mode graphique, avec cinq systèmes virtualisés fonctionnant par-dessus, à la fois des conteneurs et des machines virtuelles complètes. La plus colorée est une machine virtuelle Windows 8.1. En face de celle-ci se trouve un système de conteneurs Oracle Linux en mode texte, comme il convient à un serveur. Un bureau graphique Debian se trouve à larrière. Un conteneur CentOS 6 et une machine virtuelle CentOS 7 sont également répertoriés comme fonctionnant dans le gestionnaire de machines virtuelles mais sans connexion à leurs consoles pour le moment.</p>
<p>Tout cela et bien dautres choses encore constituent notre objectif. Nous voulons contrôler plusieurs systèmes dexploitation fonctionnant selon plusieurs modes de fonctionnement, et ce, par le biais dune interface commune.</p>
<p>Nous voulons pouvoir le faire dans un centre de données, où les conteneurs et les machines virtuelles sont répartis sur plusieurs plateformes physiques, mais nous pouvons facilement les contrôler tous à partir dune console unique. De la ligne de commande pour lefficacité et lévolutivité par le biais de scripts, et dune interface graphique pour une visibilité et un aperçu faciles.</p>
<p><img src="/images/virtualization-screen-50pc.png" alt=""></p>
<p>Nous devons dabord nous débarrasser de certains termes. Cela peut être un peu déroutant, mais si nous ny parvenons pas, nous risquons de faire le contraire de ce que nous voulons.</p>
<p>La <strong>virtualisation complète</strong> fait que lhyperviseur présente des unités centrales virtuelles, de la mémoire, une mise en réseau et bien plus encore, et le système dexploitation invité est convaincu quil fonctionne sur du matériel réel. Lhyperviseur doit effectuer une traduction binaire dynamique pour y parvenir. Il prétend être le matériel réel, idéalement en temps réel, mais bien sûr avec une certaine latence supplémentaire.</p>
<p>La <strong>paravirtualisation</strong> consiste à ce que le système dexploitation invité utilise des pilotes de périphériques personnalisés pour accéder au matériel virtualisé. Le système dexploitation invité nest pas seulement conscient quil fonctionne en plus de la virtualisation, il utilise des pilotes différents pour fonctionner un peu différemment. Voir les systèmes Linux fonctionnant sur des hyperviseurs Xen, par exemple. LEC2 (ou Elastic Compute Cloud) dAmazon Web Services est un système Linux sur Xen. Les périphériques de disque ne sont pas les habituels /dev/sd* mais plutôt /dev/xvd*, ainsi nommés par le pilote de périphérique de paravirtualisation pour les périphériques de disque virtualisés Xen.</p>
<p>La virtualisation complète peut fournir la plus grande compartimentation entre les machines virtuelles et entre les machines virtuelles et lhôte. Cest-à-dire la plus grande séparation des systèmes de fichiers, des tables de processus et des identités ou des références UID/GID. Lhyperviseur peut même émuler différents matériels - peut-être que linvité est Android sur ARM alors que lhôte est Linux sur x86_64. Comme vous pouvez déjà limaginer, la virtualisation complète, en particulier des architectures étrangères, est plus lente.</p>
<p>La virtualisation assistée par le matériel (<strong>Hardware-assisted virtualization</strong>) est un jeu dinstructions étendu qui permet au processeur deffectuer une partie du travail de virtualisation directement dans le matériel au lieu des processus de lhyperviseur dans lespace utilisateur. La virtualisation matérielle existe au moins depuis les systèmes IBM CP-40 des années 1960, mais larchitecture Intel/AMD x86 ne la ajoutée que vers 2005. Intel appelle sa version VT-x, AMD est AMD-V. Cherchez la fonctionnalité dans /proc/cpuinfo, la notion de CPU du noyau. Vous devriez trouver vmx sur les processeurs Intel et svm sur AMD. Vous en avez besoin car la virtualisation est beaucoup plus lente sans elle.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>egrep 'vmx|svm' /proc/cpuinfo
</code></pre></div></div>
<p>Avec la virtualisation assistée par matériel, les performances peuvent atteindre 95 à 99 % de la vitesse du métal nu.</p>
<p>On parle de <strong>virtualisation hybride</strong> lorsque la virtualisation assistée par matériel prend en charge la paravirtualisation.</p>
<p>Enfin, la <strong>virtualisation des systèmes dexploitation</strong> est devenue populaire sous Linux. Il y a un noyau unique partagé par le système hôte et les systèmes invités, mais un certain cloisonnement des processus, de la mémoire et du système de fichiers. <br>
Les <strong>Linux Containers (LXC)</strong> sont une technologie de virtualisation, tandis que <strong>Docker</strong> est un système de gestion et de déploiement efficace et évolutif pour LXC. Plus récemment, Docker a ajouté sa propre technologie de virtualisation pour améliorer les performances.</p>
<p>Nous pouvons les placer sur un spectre allant de la meilleure performance à la meilleure compartimentation <br>
<img src="/images/virtualisation001.png" alt=""></p>
<p><strong>Chroot</strong> existe depuis des lustres, mais ce nest que récemment quil a été largement considéré comme une forme de virtualisation. Les <strong>Linux Containers</strong> ou <strong>LXC</strong> ont leur propre UID/GID et espace de processus, et <strong>Docker</strong> est un système permettant de gérer efficacement les conteneurs et de partager leur conception. <strong>KVM</strong> ou <strong>Kernel-Based Virtual Machine</strong> est une fonctionnalité fournie par le noyau, et <strong>QEMU</strong> est le mécanisme de lespace utilisateur pour contrôler les machines virtuelles basées sur KVM. Les deux sont utilisées que vous fassiez de la paravirtualisation ou de la virtualisation complète du système, mais la QEMU est beaucoup plus manifestement impliquée dans la virtualisation complète, surtout lorsquil sagit démuler des architectures étrangères.</p>
<h2 id="chroot">chroot</h2>
<p>Vous devez exécuter chroot en tant que root de lutilisateur. Vous pouvez simplement nommer le nouveau système de fichiers racine et il exécutera le programme /bin/sh en son sein :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># chroot /var/chroots/myproject
</code></pre></div></div>
<p>Vous pouvez spécifier dautres références UID/GID non racine :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># chroot --userspec=fred:wheel /var/chroots/myproject
</code></pre></div></div>
<p>Vous pouvez nommer la racine du chroot et aussi le chemin daccès au programme à exécuter à lintérieur de celui-ci :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># chroot /var/www /sbin/httpd
</code></pre></div></div>
<p>Notez que ce qui précède implique que si <code class="language-plaintext highlighter-rouge">/var/www/sbin/httpd</code> existe, et quil sagit soit dun binaire lié statiquement, soit que <code class="language-plaintext highlighter-rouge">/var/www/lib</code> est peuplé des bibliothèques partagées nécessaires qui se trouvent généralement dans <code class="language-plaintext highlighter-rouge">/lib</code>. De plus, la racine web se trouvera sous le nouveau système de fichiers, donc les pages web dans <code class="language-plaintext highlighter-rouge">/var/www/var/www/htdocs/*</code>, les fichiers de log dans <code class="language-plaintext highlighter-rouge">/var/www/var/www/log/*</code>, et ainsi de suite. Et, très probablement, <code class="language-plaintext highlighter-rouge">/var/www/dev/</code> est partiellement peuplé de nœuds de périphériques, au moins avec <code class="language-plaintext highlighter-rouge">null</code> et <code class="language-plaintext highlighter-rouge">random</code>.</p>
<p>La famille UNIX a le chroot depuis des lustres, et ce nest quavec lintérêt récemment croissant pour la virtualisation basée sur Linux quelle a été considérée comme autre chose quune pure compartimentation.</p>
<h2 id="lxc">lxc</h2>
<p><a href="https://libvirt.org/drvlxc.html#usageConvert">LXC container driver</a></p>
<p>Les Linux Containers ou LXC sont un domaine qui évolue rapidement, et les commandes peuvent varier dune plate-forme à lautre. Par exemple, comparez lxc-ls sur Debian et Red Hat :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lxc-ls <span class="nt">--version</span>
1.0.7
<span class="nv">$ </span>lxc-ls <span class="nt">--fancy</span>
NAME STATE IPV4 IPV6 AUTOSTART
<span class="nt">------------------------------------------------</span>
container1 RUNNING 10.0.3.192 - NO
container2 STOPPED - - NO
<span class="nv">$ </span>lxc-info <span class="nt">-n</span> container1
Name: container1
State: RUNNING
PID: 4140
IP: 10.0.3.192
CPU use: 0.66 seconds
BlkIO use: 72.00 KiB
Memory use: 3.22 MiB
KMem use: 0 bytes
Link: veth22AUQI
TX bytes: 1.68 KiB
RX bytes: 5.33 KiB
Total bytes: 7.01 KiB
<span class="nv">$ </span>lxc-info <span class="nt">-n</span> container2
Name: container2
State: STOPPED
</code></pre></div></div>
<p>Cest sur Debian, où /usr/bin/lxc-ls est un script Python de 15 958 octets. Sur Red Hat, il provient dun paquet du même nom et de la même version, lxc et 1.0.7, mais cest un script shell POSIX de 2 837 octets et la commande lxc-ls version donne un rapport sur la version de ls !</p>
<p>Consultez les pages du manuel, essayez dexécuter la commande avec les paramètres de juste -h et help, et sachez quune autre syntaxe peut également être acceptée. Par exemple, au moins avec la version 1.0.7 sur Red Hat Enterprise Linux 7, ces trois commandes sont équivalentes et la seconde nest pas mentionnée dans la page de manuel :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># lxc-info -n mycontainer</span>
<span class="c"># lxc-info --name mycontainer</span>
<span class="c"># lxc-info --name=mycontainer </span>
</code></pre></div></div>
<p>Le texte qui suit utilise la syntaxe -n la plus courte, et utilise le nom pour le nom du conteneur spécifié. La plupart des commandes ont plus doptions, ce qui suit en montre suffisamment pour vous aider à démarrer.</p>
<h3 id="commandes-lxc-courantes">Commandes LXC courantes</h3>
<p><img src="/images/virtualisation002.png" alt=""></p>
<h2 id="fichiers-xml-lxc">Fichiers XML LXC</h2>
<p>Un conteneur est défini par le fichier de configuration <code class="language-plaintext highlighter-rouge">/var/lib/lxc/name/config</code>, qui contient des commentaires (de “#” à la fin de la ligne) et des attributions de valeur pour les caractéristiques simples.</p>
<p>Le système libvirt, décrit ci-dessous, peut prendre en charge la conversion de la configuration native LXC en une description XML avec cette syntaxe. Là encore, cela dépend des capacités de la version que vous avez installée :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># virsh -c lxc:///system domxml-from-native lxc-tools /var/lib/lxc/debian-10/config
</code></pre></div></div>
<p>Si tel est le cas, le résultat serait le suivant. Sinon, le fichier XML approprié peut être créé à la main :</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;domain</span> <span class="na">type=</span><span class="s">'lxc'</span><span class="nt">&gt;</span>
<span class="nt">&lt;name&gt;</span>debian-10<span class="nt">&lt;/name&gt;</span>
<span class="nt">&lt;uuid&gt;</span>a29515e3-1f37-4de8-a004-40d3e3fcf9fd<span class="nt">&lt;/uuid&gt;</span>
<span class="nt">&lt;memory</span> <span class="na">unit=</span><span class="s">'KiB'</span><span class="nt">&gt;</span>65536<span class="nt">&lt;/memory&gt;</span>
<span class="nt">&lt;currentMemory</span> <span class="na">unit=</span><span class="s">'KiB'</span><span class="nt">&gt;</span>65536<span class="nt">&lt;/currentMemory&gt;</span>
<span class="nt">&lt;vcpu</span> <span class="na">placement=</span><span class="s">'static'</span><span class="nt">&gt;</span>1<span class="nt">&lt;/vcpu&gt;</span>
<span class="nt">&lt;os&gt;</span>
<span class="nt">&lt;type&gt;</span>exe<span class="nt">&lt;/type&gt;</span>
<span class="nt">&lt;init&gt;</span>/sbin/init<span class="nt">&lt;/init&gt;</span>
<span class="nt">&lt;/os&gt;</span>
<span class="nt">&lt;features&gt;</span>
<span class="nt">&lt;capabilities</span> <span class="na">policy=</span><span class="s">'allow'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/features&gt;</span>
<span class="nt">&lt;clock</span> <span class="na">offset=</span><span class="s">'utc'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;on_poweroff&gt;</span>destroy<span class="nt">&lt;/on_poweroff&gt;</span>
<span class="nt">&lt;on_reboot&gt;</span>restart<span class="nt">&lt;/on_reboot&gt;</span>
<span class="nt">&lt;on_crash&gt;</span>destroy<span class="nt">&lt;/on_crash&gt;</span>
<span class="nt">&lt;devices&gt;</span>
<span class="nt">&lt;emulator&gt;</span>/usr/lib/libvirt/libvirt_lxc<span class="nt">&lt;/emulator&gt;</span>
<span class="nt">&lt;filesystem</span> <span class="na">type=</span><span class="s">'mount'</span> <span class="na">accessmode=</span><span class="s">'passthrough'</span><span class="nt">&gt;</span>
<span class="nt">&lt;source</span> <span class="na">dir=</span><span class="s">'dir:/var/lib/lxc/debian-10/rootfs'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;target</span> <span class="na">dir=</span><span class="s">'/'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/filesystem&gt;</span>
<span class="nt">&lt;interface</span> <span class="na">type=</span><span class="s">'bridge'</span><span class="nt">&gt;</span>
<span class="nt">&lt;mac</span> <span class="na">address=</span><span class="s">'00:16:3e:a2:cd:45'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;source</span> <span class="na">bridge=</span><span class="s">'lxcbr0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;link</span> <span class="na">state=</span><span class="s">'up'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/interface&gt;</span>
<span class="nt">&lt;/devices&gt;</span>
<span class="nt">&lt;/domain&gt;</span>
</code></pre></div></div>
<h2 id="docker">Docker</h2>
<p><a href="https://www.docker.com/">Docker</a> est un mécanisme de gestion et de partage des conteneurs. Il utilise l<a href="http://unionfs.filesystems.org/">UnionFS</a> pour utiliser très efficacement le stockage. Il a été initialement construit sur LXC, mais il utilise maintenant par défaut son propre mécanisme de virtualisation du système dexploitation libcontainer. On sattend à ce que vous utilisiez Docker pour créer des conteneurs légers dédiés à des tâches uniques spécialisées, cest pourquoi il utilise le copy-on-write pour créer très rapidement ces nouveaux conteneurs.</p>
<p>Une image Docker est un modèle à partir duquel un conteneur peut être construit. Un registre est un point de distribution dimages, analogue à un dépôt ou un repo RPM ou APT. Elles peuvent être publiques, partagées avec lInternet ou conservées dans la sphère privée de votre organisation. Vous pouvez extraire une image dun registre et lexécuter sur la machine locale. Si vous apportez des améliorations à ce conteneur, vous pouvez alors pousser votre nouvelle image vers un registre pour que dautres puissent lutiliser.</p>
<p>Disons que notre organisation a stocké une image appelée centos, un conteneur construit à partir dune installation CentOS de base. Je pourrais lintégrer à mon système et y apporter des modifications, en ajoutant le serveur web Apache et en mettant en place un système de journalisation supplémentaire, et ensuite pousser le résultat dans notre registre privé. Vous pourriez ensuite retirer mon image et ajouter une base de données en arrière-plan, puis pousser votre résultat dans le registre.</p>
<p>Un autre membre de notre organisation pourrait ensuite récupérer votre image et lexécuter. Il semble que notre registre doive croître rapidement, avec trois images disponibles - loriginal, loriginal plus mon serveur web et la journalisation, et cela plus votre travail sur la base de données. Mais seule limage initiale sera une image complète. Mes modifications, et vos modifications aux miennes, sont stockées comme les changements nécessaires pour construire limage souhaitée.</p>
<h3 id="commandes-communes-docker">Commandes communes docker</h3>
<p><img src="/images/virtualisation003.png" alt=""></p>
<p>Vous pouvez également utiliser un Dockerfile, analogue à un Makefile, pour spécifier comment créer une nouvelle image en modifiant et en démarrant une image existante.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># cat /path/to/source/directory/Dockerfile </span>
<span class="c">## Dockerfile to quickly generate a web server</span>
<span class="c">##</span>
<span class="c">## Start with this base image:</span>
FROM centos
<span class="c">## Run this command before committing the image:</span>
RUN yum <span class="nt">-y</span> <span class="nb">install </span>httpd
<span class="c">## Connect this shared volume on the host</span>
<span class="c">## containing an Apache configuration and logs:</span>
VOLUME <span class="o">[</span><span class="s2">"/var/www"</span><span class="o">]</span>
<span class="c">## Load a web site into the image file system:</span>
ADD site1.tar.gz /var/www/htdocs
<span class="c">## Set an environment variable within the container:</span>
ENV APACHE_HOME /var/www
<span class="c">## Run this command when starting the container:</span>
CMD <span class="o">[</span><span class="s2">"/usr/sbin/httpd"</span><span class="o">]</span>
</code></pre></div></div>
<p>Nous pourrions maintenant construire un conteneur basé sur ce Dockerfile. Nous lui donnerons un dépôt, un nom et une étiquette pleinement qualifiés et significatifs avec loption -t. Dans cet exemple, il sagit de la version 1.0 (ou tag), nommée serveur web dans le dépôt du projet.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># docker build -t project/webserver:1.0
</code></pre></div></div>
<h2 id="qemu">QEMU</h2>
<p>QEMU est un émulateur de machine générique à source ouverte. Il effectue une virtualisation complète du système, où vous exécutez un noyau séparé sur la VM et ce noyau voit une plate-forme complète avec votre nombre spécialisé de processeurs, la quantité de RAM, les disques et les interfaces Ethernet, USB et audio. La QEMU peut émuler une architecture différente. Vous pouvez exécuter une commande QEMU sur votre plate-forme Linux/x86_64 et la faire émuler : un système ARM sur lequel vous exécutez Android, ou un UltraSPARC sur lequel vous exécutez Solaris, ou simplement une plate-forme x86_64 sur laquelle vous exécutez Windows.</p>
<p>Si vous êtes sur du matériel x86_64, le démarrage dune machine virtuelle avec les commandes virsh ou virt-install décrites ci-dessous lance en fait un processus qemu-system-x86_64 pour émuler la plate-forme de la machine virtuelle.</p>
<p>Pour émuler une architecture différente, vous devez exécuter la commande qemu-system-* et les paramètres appropriés. Par exemple, disons que vous voulez exécuter Android sur un ARM émulé, et que vous voulez que le terminal dans lequel vous le démarrez soit connecté à une console série en plus de linterface graphique Android qui apparaîtra dans une nouvelle fenêtre. Vous devez le faire :</p>
<p>Préciser la variante ARM spécifique et la quantité de RAM.
Transmettre le noyau et les fichiers dimage disque de la RAM initiale.
Spécifier le fichier contenant limage disque, et indiquer quil doit apparaître sur une carte mémoire SD.
Préciser que la console Android est le premier port série, et que le port série est (virtuellement) connecté à votre terminal.
Assemblé, cela devient :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># qemu-system-arm -machine vexpress-a9 -m 1024\</span>
<span class="nt">-kernel</span> vmlinuz <span class="nt">-initrd</span> initrd.gz <span class="se">\</span>
<span class="nt">-drive</span> <span class="nv">file</span><span class="o">=</span>android-disk.qcow2,if<span class="o">=</span>sd <span class="se">\</span>
<span class="nt">-append</span> <span class="s2">"init=/init androidboot.console=ttyAMA0"</span> <span class="nt">-serial</span> stdio
</code></pre></div></div>
<p>Recherchez soigneusement les détails, et soyez conscient que les détails varient non seulement dune architecture à lautre mais aussi dune version des outils de lUEMQ à lautre. <a href="https://people.debian.org/~aurel32/qemu/">Cette page</a> montre comment une personne a réussi à faire fonctionner Fedora sur une grande variété darchitectures, dont AMD64, ARMel, ARMhf, i386, MIPS, PowerPC et SPARC, et fournit des images de disque téléchargeables au format <a href="https://en.wikipedia.org/wiki/Qcow">qcow2</a>.</p>
<h2 id="libvirt-et-virsh">libvirt et virsh</h2>
<p><a href="https://libvirt.org/">libvirt</a> est une boîte à outils permettant dinteragir avec les capacités de virtualisation de Linux. Elle comprend des outils en ligne de commande, une interface utilisateur graphique et une interface de <a href="https://libvirt.org/bindings.html">programmation dapplications ou API</a> pour C/C++, C#, Java, Perl, Python, PHP, Ruby et OCaml afin que vous puissiez écrire vos propres outils en ligne de commande ou graphiques. libvirt peut contrôler une grande variété de technologies de virtualisation - KVM/QEMU, LXC, Xen, VirtualBox, OpenVZ, et plus encore.</p>
<p>libvirt fait référence aux hyperviseurs au format URI :
<code class="language-plaintext highlighter-rouge">scheme://[[utilisateur@]nom d'hôte]/[session][?option1=valeur1[&amp;option2=valeur2]...]</code>
Par exemple :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lxc:///
xen:///
vmwareplayer:///session
qemu+ssh://root@somehostname/system
qemu+tls://somehostname/system
esx://somehostname/?transport=http
</code></pre></div></div>
<p>Dans ces URIs, la <strong>session</strong> se réfère généralement à laccès de lutilisateur à lhyperviseur et du <strong>system</strong> à lhyperviseur du système.</p>
<p>La commande <code class="language-plaintext highlighter-rouge">virsh</code> est une interface en ligne de commande de libvirt qui permet de tout faire. Utilisez <code class="language-plaintext highlighter-rouge">-c URI</code> pour spécifier la connexion à lhyperviseur. Par exemple, <code class="language-plaintext highlighter-rouge">list --all</code> énumérera toutes les machines virtuelles, en cours dexécution ou non, connues de lhyperviseur.</p>
<p>Cela signifie que nous avons deux manières équivalentes de manipuler les conteneurs :</p>
<h3 id="opérations-communes-sur-les-conteneurs-en-virsh-et-lxc-">Opérations communes sur les conteneurs en virsh et lxc-*</h3>
<p><img src="/images/virtualisation004.png" alt=""></p>
<p>Les conteneurs fonctionnent sous le noyau hôte, le “redémarrage” ou larrêt de ces derniers ne contrôle que les processus sexécutant à lintérieur du conteneur.</p>
<p>La virtualisation complète du système avec QEMU et KVM signifie un noyau séparé. Plusieurs commandes virsh ont des analogies avec les actions effectuées sur une machine physique :</p>
<h3 id="analogies-des-commandes-virsh">Analogies des commandes virsh</h3>
<p><img src="/images/virtualisation005.png" alt=""></p>
<p><code class="language-plaintext highlighter-rouge">virt-manager</code> est un bel outil graphique dans lequel vous pouvez surveiller, contrôler et créer des systèmes virtualisés. Utilisez le choix de menu <code class="language-plaintext highlighter-rouge">Fichier | Ajouter une connexion</code> pour ajouter des connexions à dautres systèmes dhyperviseur. Dans cette vue, <code class="language-plaintext highlighter-rouge">lxc:///</code> et <code class="language-plaintext highlighter-rouge">qemu:///system</code> sont utilisés.</p>
<p><img src="/images/virtualization-cropped.png" alt=""></p>
<blockquote>
<p>AVIS : Avec la sortie de RHEL 8, Red Hat a déprécié virt-manager au profit de linterface de gestion basée sur le navigateur Cockpit qui écoute sur TCP/9090.</p>
</blockquote>
<h2 id="fichiers-xml-libvirt">Fichiers XML libvirt</h2>
<p>Les machines virtuelles sont décrites par XML. Vous pouvez transférer la description XML vers une sortie standard avec <code class="language-plaintext highlighter-rouge">virsh dumpxml</code></p>
<p>Vous pouvez enregistrer cette sortie XML dans un fichier, modifier le fichier pour changer le <code class="language-plaintext highlighter-rouge">name</code>, supprimer <code class="language-plaintext highlighter-rouge">uuid</code> et toutes les valeurs <code class="language-plaintext highlighter-rouge">adresse mac</code>, puis définir une nouvelle machine virtuelle avec avec <code class="language-plaintext highlighter-rouge">virsh define</code></p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;domain</span> <span class="na">type=</span><span class="s">'kvm'</span><span class="nt">&gt;</span>
<span class="nt">&lt;name&gt;</span>myvm<span class="nt">&lt;/name&gt;</span>
<span class="nt">&lt;uuid&gt;</span>8b9ad37b-9ecb-444f-9f28-298f0bbad29b<span class="nt">&lt;/uuid&gt;</span>
<span class="nt">&lt;memory</span> <span class="na">unit=</span><span class="s">'KiB'</span><span class="nt">&gt;</span>524288<span class="nt">&lt;/memory&gt;</span>
<span class="nt">&lt;currentMemory</span> <span class="na">unit=</span><span class="s">'KiB'</span><span class="nt">&gt;</span>524288<span class="nt">&lt;/currentMemory&gt;</span>
<span class="nt">&lt;vcpu</span> <span class="na">placement=</span><span class="s">'static'</span><span class="nt">&gt;</span>1<span class="nt">&lt;/vcpu&gt;</span>
<span class="nt">&lt;os&gt;</span>
<span class="nt">&lt;type</span> <span class="na">arch=</span><span class="s">'x86_64'</span> <span class="na">machine=</span><span class="s">'pc-i440fx-rhel7.0.0'</span><span class="nt">&gt;</span>hvm<span class="nt">&lt;/type&gt;</span>
<span class="nt">&lt;boot</span> <span class="na">dev=</span><span class="s">'hd'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/os&gt;</span>
<span class="nt">&lt;features&gt;</span>
<span class="nt">&lt;acpi/&gt;</span>
<span class="nt">&lt;apic/&gt;</span>
<span class="nt">&lt;pae/&gt;</span>
<span class="nt">&lt;/features&gt;</span>
<span class="nt">&lt;clock</span> <span class="na">offset=</span><span class="s">'utc'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;on_poweroff&gt;</span>destroy<span class="nt">&lt;/on_poweroff&gt;</span>
<span class="nt">&lt;on_reboot&gt;</span>restart<span class="nt">&lt;/on_reboot&gt;</span>
<span class="nt">&lt;on_crash&gt;</span>restart<span class="nt">&lt;/on_crash&gt;</span>
<span class="nt">&lt;devices&gt;</span>
<span class="nt">&lt;emulator&gt;</span>/usr/libexec/qemu-kvm<span class="nt">&lt;/emulator&gt;</span>
<span class="nt">&lt;disk</span> <span class="na">type=</span><span class="s">'file'</span> <span class="na">device=</span><span class="s">'disk'</span><span class="nt">&gt;</span>
<span class="nt">&lt;driver</span> <span class="na">name=</span><span class="s">'qemu'</span> <span class="na">type=</span><span class="s">'qcow2'</span> <span class="na">cache=</span><span class="s">'none'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;source</span> <span class="na">file=</span><span class="s">'/usr/local/containers/myvm.qcow2'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;target</span> <span class="na">dev=</span><span class="s">'vda'</span> <span class="na">bus=</span><span class="s">'virtio'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bus=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x05'</span> <span class="na">function=</span><span class="s">'0x0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/disk&gt;</span>
<span class="nt">&lt;controller</span> <span class="na">type=</span><span class="s">'usb'</span> <span class="na">index=</span><span class="s">'0'</span><span class="nt">&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bus=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x01'</span> <span class="na">function=</span><span class="s">'0x2'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/controller&gt;</span>
<span class="nt">&lt;controller</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">index=</span><span class="s">'0'</span> <span class="na">model=</span><span class="s">'pci-root'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;controller</span> <span class="na">type=</span><span class="s">'virtio-serial'</span> <span class="na">index=</span><span class="s">'0'</span><span class="nt">&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bus=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x04'</span> <span class="na">function=</span><span class="s">'0x0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/controller&gt;</span>
<span class="nt">&lt;interface</span> <span class="na">type=</span><span class="s">'network'</span><span class="nt">&gt;</span>
<span class="nt">&lt;mac</span> <span class="na">address=</span><span class="s">'52:54:00:7a:fe:26'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;source</span> <span class="na">network=</span><span class="s">'default'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;model</span> <span class="na">type=</span><span class="s">'virtio'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bux=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x03'</span> <span class="na">function=</span><span class="s">'0x0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/interface&gt;</span>
<span class="nt">&lt;serial</span> <span class="na">type=</span><span class="s">'pty'</span><span class="nt">&gt;</span>
<span class="nt">&lt;target</span> <span class="na">port=</span><span class="s">'0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/serial&gt;</span>
<span class="nt">&lt;console</span> <span class="na">type=</span><span class="s">'pty'</span><span class="nt">&gt;</span>
<span class="nt">&lt;target</span> <span class="na">type=</span><span class="s">'serial'</span> <span class="na">port=</span><span class="s">'0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/console&gt;</span>
<span class="nt">&lt;channel</span> <span class="na">type=</span><span class="s">'spicevmc'</span><span class="nt">&gt;</span>
<span class="nt">&lt;target</span> <span class="na">type=</span><span class="s">'virtio'</span> <span class="na">name=</span><span class="s">'com.redhat.spice.0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'virtio-serial'</span> <span class="na">controller=</span><span class="s">'0'</span> <span class="na">bus=</span><span class="s">'0'</span> <span class="na">port=</span><span class="s">'1'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/channel&gt;</span>
<span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">'tablet'</span> <span class="na">bus=</span><span class="s">'usb'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">'mouse'</span> <span class="na">bus=</span><span class="s">'usb'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;graphics</span> <span class="na">type=</span><span class="s">'spice'</span> <span class="na">autoport=</span><span class="s">'yes'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;video&gt;</span>
<span class="nt">&lt;model</span> <span class="na">type=</span><span class="s">'qxl'</span> <span class="na">ram=</span><span class="s">'65536'</span> <span class="na">vram=</span><span class="s">'65536'</span> <span class="na">heads=</span><span class="s">'1'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bus=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x02'</span> <span class="na">function=</span><span class="s">'0x0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/video&gt;</span>
<span class="nt">&lt;memballoon</span> <span class="na">model=</span><span class="s">'virtio'</span><span class="nt">&gt;</span>
<span class="nt">&lt;address</span> <span class="na">type=</span><span class="s">'pci'</span> <span class="na">domain=</span><span class="s">'0x0000'</span> <span class="na">bus=</span><span class="s">'0x00'</span> <span class="na">slot=</span><span class="s">'0x06'</span> <span class="na">function=</span><span class="s">'0x0'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/memballoon&gt;</span>
<span class="nt">&lt;/devices&gt;</span>
<span class="nt">&lt;/domain&gt;</span>
</code></pre></div></div>
<h2 id="libvirt-réseau">libvirt Réseau</h2>
<p>Il existe une commande <code class="language-plaintext highlighter-rouge">virsh net-dumpxml</code> similaire, qui donne la définition XML dun réseau virtuel.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;network&gt;</span>
<span class="nt">&lt;name&gt;</span>default<span class="nt">&lt;/name&gt;</span>
<span class="nt">&lt;uuid&gt;</span>f37ba69f-6b2c-4775-82d4-6f03a392acb7<span class="nt">&lt;/uuid&gt;</span>
<span class="nt">&lt;forward</span> <span class="na">mode=</span><span class="s">'nat'</span><span class="nt">&gt;</span>
<span class="nt">&lt;nat&gt;</span>
<span class="nt">&lt;port</span> <span class="na">start=</span><span class="s">'1024'</span> <span class="na">end=</span><span class="s">'65535'</span><span class="nt">/&gt;</span>
<span class="nt">&lt;/nat&gt;</span>
<span class="nt">&lt;/forward&gt;</span>
<span class="nt">&lt;bridge</span> <span class="na">name=</span><span class="s">'virbr0'</span> <span class="na">stp=</span><span class="s">'on'</span> <span class="na">delay=</span><span class="s">'0'</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;ip</span> <span class="na">address=</span><span class="s">'192.168.122.1'</span> <span class="na">netmask=</span><span class="s">'255.255.255.0'</span><span class="nt">&gt;</span>
<span class="nt">&lt;dhcp&gt;</span>
<span class="nt">&lt;range</span> <span class="na">start=</span><span class="s">'192.168.122.2'</span> <span class="na">end=</span><span class="s">'192.168.122.254'</span> <span class="nt">/&gt;</span>
<span class="nt">&lt;/dhcp&gt;</span>
<span class="nt">&lt;/ip&gt;</span>
<span class="nt">&lt;/network&gt;</span>
</code></pre></div></div>
<p>Disons que nous navons jusquà présent quun seul réseau et un seul pont virtuel :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># virsh net-list --all </span>
Name State Autostart Persistent
<span class="nt">----------------------------------------------------------</span>
default active <span class="nb">yes yes</span>
<span class="c"># brctl show</span>
bridge name bridge <span class="nb">id </span>STP enabled interfaces
docker0 8000.56847afe9799 no
virbr0 8000.000000000000 <span class="nb">yes</span>
</code></pre></div></div>
<p>Nous pouvons transférer la description XML du réseau virtualisé dans un fichier, léditer et définir un nouveau réseau :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># virsh net-dumpxml default &gt; /tmp/newnet.xml</span>
<span class="c"># vim /tmp/newnet.xml</span>
<span class="o">[</span>... change name from <span class="s2">"default"</span> to <span class="s2">"newnet"</span>,
virtual bridge name from <span class="s2">"virbr0"</span> to <span class="s2">"virbr1"</span>,
and IP address range to 192.168.123.0/24 ...]
<span class="c"># virsh net-define /tmp/newnet.xml </span>
Network newnet defined from /tmp/mynet.xml
<span class="c"># virsh net-list --all </span>
Name State Autostart Persistent
<span class="nt">----------------------------------------------------------</span>
default active <span class="nb">yes yes
</span>newnet inactive no <span class="nb">yes</span>
<span class="c"># brctl show</span>
bridge name bridge <span class="nb">id </span>STP enabled interfaces
docker0 8000.56847afe9799 no
virbr0 8000.000000000000 <span class="nb">yes</span>
</code></pre></div></div>
<p>Le nouveau réseau est là, mais pas le nouveau pont virtuel. Nous devons lancer le nouveau réseau.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># virsh net-start newnet </span>
Network newnet started
<span class="c"># virsh net-autostart newnet </span>
Network newnet marked as autostarted
<span class="c"># virsh net-list --all </span>
Name State Autostart Persistent
<span class="nt">----------------------------------------------------------</span>
default active <span class="nb">yes yes
</span>newnet active <span class="nb">yes yes</span>
<span class="c"># brctl show</span>
bridge name bridge <span class="nb">id </span>STP enabled interfaces
docker0 8000.56847afe9799 no
virbr0 8000.000000000000 <span class="nb">yes
</span>virbr1 8000.52540016cb49 <span class="nb">yes </span>virbr1-nic
</code></pre></div></div>
</div>
<div class="d-print-none">
<footer class="article__footer"><meta itemprop="dateModified" content="2020-10-03T00:00:00+02:00">
<!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix">
<div class="previous">
<span>PRÉCÉDENT</span><a href="/2020/09/30/Samba-Partage-de-fichiers.html">Partage de fichiers hôte linux et invité windows avec Samba</a>
</div>
<div class="next">
<span>SUIVANT</span><a href="/2020/10/04/Comparaison-fournisseurs-VPN.html">Comparaison fournisseurs VPN</a>
</div>
</div>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div>
<section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal">
<script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text">
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>