Configure cloudimage-based nodes from devops template

- New attributes in Libvirt Node object:
  cloud_init_volume_name: <string>  # Volume name for cloudinit config
      If set, than Node.define() will try to make an ISO image with
      metadata for cloudimage, and upload it to the specified Volume.
  cloud_init_iface_up: <string>  # Interface label to set up on boot

- New attributes in Libvirt Volume object for cloudinit metadata:
  cloudinit_meta_data: <string>  # Yaml as a string
  cloudinit_user_data: <string>  # Yaml as a string

- Resize 'source_image' after upload to the specified 'capacity' size.
  It's useful for cases when original qcow2 is much smaller than
  it is required, so 'source_image' with the size 2Gb will be resized
  to 'capacity', for example 50Gb, after upload to the libvirt pool.
  For qcow2 images, real disk space is not allocated, it is a virtual
  resize only.

- 'centos_master' node role extension refactored: now pre_define()
  method just initialize the metadata objects (if they weren't passed
  from the template).

- add an example template

Closes-Bug:#1602298

Change-Id: If35b252964a28a4bd43f073318cee93f3dfbe907
This commit is contained in:
Dennis Dmitriev 2016-07-06 21:42:57 +03:00
parent ac9c27039a
commit b5625c1f2f
8 changed files with 542 additions and 114 deletions

View File

@ -15,12 +15,14 @@
import datetime
import itertools
import os
import shutil
import subprocess
from time import sleep
import uuid
from warnings import warn
import xml.etree.ElementTree as ET
from django.conf import settings
from django.utils.functional import cached_property
import libvirt
@ -28,6 +30,7 @@ import netaddr
from devops.driver.libvirt.libvirt_xml_builder import LibvirtXMLBuilder
from devops.error import DevopsError
from devops.helpers.cloud_image_settings import generate_cloud_image_settings
from devops.helpers.helpers import deepgetattr
from devops.helpers.helpers import get_file_size
from devops.helpers.helpers import underscored
@ -679,6 +682,8 @@ class LibvirtVolume(Volume):
serial = ParamField()
wwn = ParamField()
multipath_count = ParamField(default=0)
cloudinit_meta_data = ParamField(default=None)
cloudinit_user_data = ParamField(default=None)
@property
def _libvirt_volume(self):
@ -702,10 +707,11 @@ class LibvirtVolume(Volume):
backing_store_path = self.backing_store.get_path()
backing_store_format = self.backing_store.format
capacity = int((self.capacity or 0) * 1024 ** 3)
if self.source_image is not None:
capacity = get_file_size(self.source_image)
else:
capacity = int(self.capacity * 1024 ** 3)
file_size = get_file_size(self.source_image)
if file_size > capacity:
capacity = file_size
pool_name = self.driver.storage_pool_name
pool = self.driver.conn.storagePoolLookupByName(pool_name)
@ -726,7 +732,7 @@ class LibvirtVolume(Volume):
# Upload predefined image to the volume
if self.source_image is not None:
self.upload(self.source_image)
self.upload(self.source_image, capacity)
@retry(libvirt.libvirtError)
def remove(self, *args, **kwargs):
@ -751,7 +757,7 @@ class LibvirtVolume(Volume):
self.format = self.get_format()
@retry(libvirt.libvirtError, count=2)
def upload(self, path):
def upload(self, path, capacity=0):
def chunk_render(_, _size, _fd):
return _fd.read(_size)
@ -772,6 +778,11 @@ class LibvirtVolume(Volume):
stream.sendAll(chunk_render, fd)
stream.finish()
if capacity > size:
# Resize the uploaded image to specified capacity
self._libvirt_volume.resize(capacity)
self.save()
def get_allocation(self):
"""Get allocated volume size
@ -837,6 +848,8 @@ class LibvirtNode(Node):
has_vnc = ParamField(default=True)
bootmenu_timeout = ParamField(default=0)
numa = ParamField(default=[])
cloud_init_volume_name = ParamField()
cloud_init_iface_up = ParamField()
@property
def _libvirt_node(self):
@ -974,6 +987,9 @@ class LibvirtNode(Node):
logger.debug(node_xml)
self.uuid = self.driver.conn.defineXML(node_xml).UUIDString()
if self.cloud_init_volume_name is not None:
self._create_cloudimage_settings_iso()
super(LibvirtNode, self).define()
def start(self):
@ -1038,6 +1054,52 @@ class LibvirtNode(Node):
def has_snapshot(self, name):
return name in self._libvirt_node.snapshotListNames()
def _create_cloudimage_settings_iso(self):
"""Builds setting iso to send basic configuration for cloud image"""
if self.cloud_init_volume_name is None:
return
volume = self.get_volume(name=self.cloud_init_volume_name)
interface = self.interface_set.get(
label=self.cloud_init_iface_up)
admin_ip = self.get_ip_address_by_network_name(
name=None, interface=interface)
env_name = self.group.environment.name
dir_path = os.path.join(settings.CLOUD_IMAGE_DIR, env_name)
cloud_image_settings_path = os.path.join(
dir_path, 'cloud_settings.iso')
meta_data_path = os.path.join(dir_path, "meta-data")
user_data_path = os.path.join(dir_path, "user-data")
interface_name = interface.label
admin_ap = interface.l2_network_device.address_pool
gateway = str(admin_ap.gateway)
admin_netmask = str(admin_ap.ip_network.netmask)
admin_network = str(admin_ap.ip_network)
hostname = self.name
generate_cloud_image_settings(
cloud_image_settings_path=cloud_image_settings_path,
meta_data_path=meta_data_path,
user_data_path=user_data_path,
admin_network=admin_network,
interface_name=interface_name,
admin_ip=admin_ip,
admin_netmask=admin_netmask,
gateway=gateway,
hostname=hostname,
meta_data_content=volume.cloudinit_meta_data,
user_data_content=volume.cloudinit_user_data,
)
volume.upload(cloud_image_settings_path)
# Clear temporary files
if os.path.exists(dir_path):
shutil.rmtree(dir_path)
# EXTERNAL SNAPSHOT
def snapshot_create_child_volumes(self, name):
for disk in self.disk_devices:

View File

@ -18,10 +18,13 @@ import subprocess
from devops import logger
def generate_cloud_image_settings(cloud_image_settings_path, admin_network,
def generate_cloud_image_settings(cloud_image_settings_path, meta_data_path,
user_data_path, admin_network,
interface_name, admin_ip, admin_netmask,
gateway, dns, dns_ext,
hostname, user, password):
gateway,
hostname,
meta_data_content=None,
user_data_content=None):
# create dir for meta_data, user_data and cloud_ISO
dir_path = os.path.dirname(cloud_image_settings_path)
@ -29,11 +32,6 @@ def generate_cloud_image_settings(cloud_image_settings_path, admin_network,
if not os.path.exists(dir_path):
os.makedirs(dir_path)
meta_data_path = os.path.join(dir_path,
"meta-data")
user_data_path = os.path.join(dir_path,
"user-data")
# create meta_data and user_data
meta_data_context = {
@ -42,21 +40,20 @@ def generate_cloud_image_settings(cloud_image_settings_path, admin_network,
"network": admin_network,
"netmask": admin_netmask,
"gateway": gateway,
"dns": dns,
"dns_ext": dns_ext,
"hostname": hostname
}
meta_data_content = ("instance-id: iid-local1\n"
"network-interfaces: |\n"
" auto {interface_name}\n"
" iface {interface_name} inet static\n"
" address {address}\n"
" network {network}\n"
" netmask {netmask}\n"
" gateway {gateway}\n"
" dns-nameservers {dns} {dns_ext}\n"
"local-hostname: {hostname}")
if meta_data_content is None:
meta_data_content = ("instance-id: iid-local1\n"
"network-interfaces: |\n"
" auto {interface_name}\n"
" iface {interface_name} inet static\n"
" address {address}\n"
" network {network}\n"
" netmask {netmask}\n"
" gateway {gateway}\n"
" dns-nameservers 8.8.8.8\n"
"local-hostname: {hostname}")
logger.debug("meta_data contains next data: \n{}".format(
meta_data_content.format(**meta_data_context)))
@ -67,23 +64,22 @@ def generate_cloud_image_settings(cloud_image_settings_path, admin_network,
user_data_context = {
"interface_name": interface_name,
"gateway": gateway,
"user": user,
"password": password
}
user_data_content = ("\n#cloud-config\n"
"ssh_pwauth: True\n"
"chpasswd:\n"
" list: |\n"
" {user}:{password}\n"
" expire: False \n\n"
"runcmd:\n"
" - sudo ifup {interface_name}\n"
" - sudo sed -i -e '/^PermitRootLogin/s/^"
".*$/PermitRootLogin yes/' /etc/ssh/sshd_config\n"
" - sudo service ssh restart\n"
" - sudo route add default gw "
"{gateway} {interface_name}")
if user_data_content is None:
user_data_content = ("\n#cloud-config\n"
"ssh_pwauth: True\n"
"chpasswd:\n"
" list: |\n"
" root:r00tme\n"
" expire: False\n\n"
"runcmd:\n"
" - sudo ifup {interface_name}\n"
" - sudo sed -i -e '/^PermitRootLogin/s/^"
".*$/PermitRootLogin yes/' /etc/ssh/sshd_config\n"
" - sudo service ssh restart\n"
" - sudo route add default gw "
"{gateway} {interface_name}")
logger.debug("user_data contains next data: \n{}".format(
user_data_content.format(**user_data_context)))

View File

@ -245,7 +245,7 @@ class Node(six.with_metaclass(ExtendableNodeType, ParamedModel, BaseModel)):
# LEGACY, for fuel-qa compatibility
@property
def is_admin(self):
return 'master' in self.role
return 'master' in str(self.role)
# LEGACY, for fuel-qa compatibility
@property

View File

@ -13,9 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import os
from devops.helpers.cloud_image_settings import generate_cloud_image_settings
from devops.helpers.helpers import wait_tcp
from devops import logger
from devops import settings
@ -43,38 +40,51 @@ class NodeExtension(object):
def get_kernel_cmd(self, **kwargs):
return None
def post_define(self):
"""Builds setting iso to send basic configuration for cloud image"""
def pre_define(self):
cloud_image_settings_path = os.path.join(
settings.CLOUD_IMAGE_DIR, 'cloud_settings.iso')
admin_ip = self.node.get_ip_address_by_network_name(
settings.SSH_CREDENTIALS['admin_network'])
interface = self.node.get_interface_by_network_name(
settings.SSH_CREDENTIALS['admin_network'])
interface_name = interface.label
user = settings.SSH_CREDENTIALS['login']
password = settings.SSH_CREDENTIALS['password']
admin_ap = interface.l2_network_device.address_pool
gateway = str(admin_ap.gateway)
admin_netmask = str(admin_ap.ip_network.netmask)
admin_network = str(admin_ap.ip_network)
dns = settings.DEFAULT_DNS
dns_ext = dns
hostname = settings.DEFAULT_MASTER_FQDN
if self.node.cloud_init_volume_name is None:
self.node.cloud_init_volume_name = 'iso'
generate_cloud_image_settings(
cloud_image_settings_path=cloud_image_settings_path,
admin_network=admin_network,
interface_name=interface_name,
admin_ip=admin_ip,
admin_netmask=admin_netmask,
gateway=gateway,
dns=dns,
dns_ext=dns_ext,
hostname=hostname,
user=user,
password=password
)
if self.node.cloud_init_iface_up is None:
interface = self.node.get_interface_by_network_name(
settings.SSH_CREDENTIALS['admin_network'])
self.node.cloud_init_iface_up = interface.label
self.node.get_volume(name='iso').upload(cloud_image_settings_path)
self.node.save()
volume = self.node.get_volume(name=self.node.cloud_init_volume_name)
if volume.cloudinit_meta_data is None:
volume.cloudinit_meta_data = (
"instance-id: iid-local1\n"
"network-interfaces: |\n"
" auto {interface_name}\n"
" iface {interface_name} inet static\n"
" address {address}\n"
" network {network}\n"
" netmask {netmask}\n"
" gateway {gateway}\n" +
" dns-nameservers {dns}\n"
"local-hostname: {hostname}".format(
dns=settings.DEFAULT_DNS,
hostname=settings.DEFAULT_MASTER_FQDN))
if volume.cloudinit_user_data is None:
volume.cloudinit_user_data = (
"\n#cloud-config\n"
"ssh_pwauth: True\n"
"chpasswd:\n"
" list: |\n" +
" {user}:{password}\n".format(
user=settings.SSH_CREDENTIALS['login'],
password=settings.SSH_CREDENTIALS['password']
) +
" expire: False\n\n"
"runcmd:\n"
" - sudo ifup {interface_name}\n"
" - sudo sed -i -e '/^PermitRootLogin/s/^"
".*$/PermitRootLogin yes/' /etc/ssh/sshd_config\n"
" - sudo service ssh restart\n"
" - sudo route add default gw "
"{gateway} {interface_name}")
volume.save()

View File

@ -0,0 +1,121 @@
# Copyright 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import mock
from devops.models import Environment
from devops.tests.driver.libvirt.base import LibvirtTestCase
from django.conf import settings
class TestCloudImage(LibvirtTestCase):
def patch(self, *args, **kwargs):
patcher = mock.patch(*args, **kwargs)
m = patcher.start()
self.addCleanup(patcher.stop)
return m
def setUp(self):
super(TestCloudImage, self).setUp()
self.open_mock = mock.mock_open(read_data='image_data')
self.patch('devops.driver.libvirt.libvirt_driver.open',
self.open_mock, create=True)
self.os_mock = self.patch('devops.helpers.helpers.os')
Size = collections.namedtuple('Size', ['st_size'])
self.file_sizes = {
'/tmp/test/cloud_settings.iso': Size(st_size=1 * 1024 ** 3),
}
self.os_mock.stat.side_effect = self.file_sizes.get
self.generate_cloud_image_settings_mock = self.patch(
'devops.driver.libvirt.libvirt_driver'
'.generate_cloud_image_settings')
self.volume_upload_mock = self.patch(
'devops.driver.libvirt.Volume.upload')
# Environment with a 'public' network
self.env = Environment.create('test')
self.group = self.env.add_group(
group_name='test_group',
driver_name='devops.driver.libvirt',
connection_string='test:///default',
storage_pool_name='default-pool')
self.pub_ap = self.env.add_address_pool(
name='public-pool01', net='10.109.0.0/16:24', tag=0,
ip_reserved=dict(gateway=1, l2_network_device=1),
ip_ranges=dict(default=[2, -2]))
self.group.add_l2_network_device(
name='public', address_pool='public-pool01')
# Node connected to the 'public' network
self.node = self.group.add_node(
name='test-node',
role='cloud-node',
architecture='x86_64',
hypervisor='test',
cloud_init_volume_name='iso',
cloud_init_iface_up='enp0s3')
self.system_volume = self.node.add_volume(name='system')
self.iso_volume = self.node.add_volume(name='iso')
self.adm_iface = self.node.add_interface(
label='enp0s3',
l2_network_device_name='public',
mac_address='64:b6:87:44:14:17',
interface_model='e1000')
self.node.add_network_config(
label='enp0s3',
networks=['public'])
@mock.patch('devops.driver.libvirt.libvirt_driver.uuid')
@mock.patch('libvirt.virConnect.defineXML')
@mock.patch.multiple(settings, CLOUD_IMAGE_DIR='/tmp/')
def test_post_define(self, define_xml_mock, uuid_mock):
uuid_mock.uuid4.side_effect = (
mock.Mock(hex='fe527bd28e0f4a84b9117dc97142c580'),
mock.Mock(hex='9cddb80fe82e480eb14c1a89f1c0e11d'),
mock.Mock(hex='885674d28e0f4a84b265625673674565'),
mock.Mock(hex='91252350fe82e480eb14c1a89f1c0234'))
define_xml_mock.return_value.UUIDString.return_value = 'fake_uuid'
self.system_volume.define()
self.iso_volume.define()
self.node.define()
self.generate_cloud_image_settings_mock.assert_called_once_with(
admin_ip='10.109.0.2',
admin_netmask='255.255.255.0',
admin_network='10.109.0.0/24',
cloud_image_settings_path='/tmp/test/cloud_settings.iso',
meta_data_content=None,
meta_data_path='/tmp/test/meta-data',
user_data_content=None,
user_data_path='/tmp/test/user-data',
gateway='10.109.0.1',
hostname='test-node',
interface_name='enp0s3')
self.volume_upload_mock.assert_called_once_with(
'/tmp/test/cloud_settings.iso')

View File

@ -50,12 +50,11 @@ class TestCloudImageSettings(unittest.TestCase):
admin_netmask='255.255.255.0',
admin_network='10.109.0.0/24',
cloud_image_settings_path='/mydir/cloud_settings.iso',
dns='8.8.8.8', dns_ext='8.8.8.8',
meta_data_path='/mydir/meta-data',
user_data_path='/mydir/user-data',
gateway='10.109.0.1',
hostname='nailgun.domain.local',
interface_name=u'enp0s3',
password='r00tme',
user='root')
interface_name=u'enp0s3')
self.os_mock.makedirs.assert_called_once_with('/mydir')
@ -71,7 +70,7 @@ class TestCloudImageSettings(unittest.TestCase):
' network 10.109.0.0/24\n'
' netmask 255.255.255.0\n'
' gateway 10.109.0.1\n'
' dns-nameservers 8.8.8.8 8.8.8.8\n'
' dns-nameservers 8.8.8.8\n'
'local-hostname: nailgun.domain.local'),
mock.call().__exit__(None, None, None),
mock.call('/mydir/user-data', 'w'),
@ -83,7 +82,7 @@ class TestCloudImageSettings(unittest.TestCase):
"chpasswd:\n"
" list: |\n"
" root:r00tme\n"
" expire: False \n"
" expire: False\n"
"\n"
"runcmd:\n"
" - sudo ifup enp0s3\n"

View File

@ -12,13 +12,16 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
import mock
from devops import settings
from devops.tests.driver.driverless import DriverlessTestCase
from devops.models import Environment
from devops.tests.driver.libvirt.base import LibvirtTestCase
from django.conf import settings
class TestCentosMasterExt(DriverlessTestCase):
class TestCentosMasterExt(LibvirtTestCase):
def patch(self, *args, **kwargs):
patcher = mock.patch(*args, **kwargs)
@ -29,17 +32,46 @@ class TestCentosMasterExt(DriverlessTestCase):
def setUp(self):
super(TestCentosMasterExt, self).setUp()
self.open_mock = mock.mock_open(read_data='image_data')
self.patch('devops.driver.libvirt.libvirt_driver.open',
self.open_mock, create=True)
self.os_mock = self.patch('devops.helpers.helpers.os')
Size = collections.namedtuple('Size', ['st_size'])
self.file_sizes = {
'/tmp/test/cloud_settings.iso': Size(st_size=1 * 1024 ** 3),
}
self.os_mock.stat.side_effect = self.file_sizes.get
# Environment with an 'admin' network
self.env = Environment.create('test')
self.group = self.env.add_group(
group_name='test_group',
driver_name='devops.driver.libvirt',
connection_string='test:///default',
storage_pool_name='default-pool')
self.pub_ap = self.env.add_address_pool(
name='admin-pool01', net='10.109.0.0/16:24', tag=0,
ip_reserved=dict(gateway=1, l2_network_device=1),
ip_ranges=dict(default=[2, -2]))
self.group.add_l2_network_device(
name='admin', address_pool='admin-pool01')
self.node = self.group.add_node(
name='test-node',
role='centos_master')
self.node.add_volume(
name='system')
self.node.add_volume(
name='iso')
role='centos_master',
architecture='x86_64',
hypervisor='test')
self.system_volume = self.node.add_volume(name='system')
self.iso_volume = self.node.add_volume(name='iso')
self.adm_iface = self.node.add_interface(
label='enp0s3',
l2_network_device_name='admin',
mac_address='64:c6:27:47:14:83',
interface_model='e1000')
self.node.add_network_config(
@ -49,37 +81,63 @@ class TestCentosMasterExt(DriverlessTestCase):
self.wait_tcp_mock = self.patch(
'devops.models.node_ext.centos_master.wait_tcp')
self.generate_cloud_image_settings_mock = self.patch(
'devops.models.node_ext.'
'centos_master.generate_cloud_image_settings')
@mock.patch('devops.driver.libvirt.libvirt_driver.uuid')
@mock.patch('libvirt.virConnect.defineXML')
@mock.patch.multiple(settings, CLOUD_IMAGE_DIR='/tmp/')
def test_001_pre_define(self, define_xml_mock, uuid_mock):
uuid_mock.uuid4.side_effect = (
mock.Mock(hex='fe527bd28e0f4a84b9117dc97142c580'),
mock.Mock(hex='9cddb80fe82e480eb14c1a89f1c0e11d'),
mock.Mock(hex='885674d28e0f4a84b265625673674565'),
mock.Mock(hex='91252350fe82e480eb14c1a89f1c0234'))
define_xml_mock.return_value.UUIDString.return_value = 'fake_uuid'
self.volume_upload_mock = self.patch(
'devops.models.volume.Volume.upload', create=True)
@mock.patch.multiple(settings, CLOUD_IMAGE_DIR='/mydir/')
def test_post_define(self):
self.system_volume.define()
self.iso_volume.define()
self.node.define()
self.generate_cloud_image_settings_mock.assert_called_once_with(
admin_ip='10.109.0.2',
admin_netmask='255.255.255.0',
admin_network='10.109.0.0/24',
cloud_image_settings_path='/mydir/cloud_settings.iso',
dns='8.8.8.8', dns_ext='8.8.8.8',
gateway='10.109.0.1',
hostname='nailgun.domain.local',
interface_name='enp0s3',
password='r00tme',
user='root')
self.volume_upload_mock.assert_called_once_with(
'/mydir/cloud_settings.iso')
def test_deploy_wait(self):
assert self.node.cloud_init_iface_up == 'enp0s3'
assert self.node.cloud_init_volume_name == 'iso'
volume = self.node.get_volume(
name=self.node.cloud_init_volume_name)
assert volume.cloudinit_meta_data == (
"instance-id: iid-local1\n"
"network-interfaces: |\n"
" auto {interface_name}\n"
" iface {interface_name} inet static\n"
" address {address}\n"
" network {network}\n"
" netmask {netmask}\n"
" gateway {gateway}\n"
" dns-nameservers 8.8.8.8\n"
"local-hostname: nailgun.domain.local")
assert volume.cloudinit_user_data == (
"\n#cloud-config\n"
"ssh_pwauth: True\n"
"chpasswd:\n"
" list: |\n"
" root:r00tme\n"
" expire: False\n\n"
"runcmd:\n"
" - sudo ifup {interface_name}\n"
" - sudo sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin yes/'"
" /etc/ssh/sshd_config\n"
" - sudo service ssh restart\n"
" - sudo route add default gw {gateway} {interface_name}")
def test_002_deploy_wait(self):
self.node.ext.deploy_wait()
def test_get_kernel_cmd(self):
def test_003_get_kernel_cmd(self):
assert self.node.ext.get_kernel_cmd() is None
def test_bootstrap_and_wait(self):
@mock.patch('devops.driver.libvirt.Node.is_active')
def test_004_bootstrap_and_wait(self, is_active_mock):
is_active_mock.return_value = True
self.node.ext.bootstrap_and_wait()
self.wait_tcp_mock.assert_called_once_with(
host='10.109.0.2', port=22, timeout=600,

View File

@ -0,0 +1,182 @@
.. _cloudinit_example.yaml:
YAML template for cloud-init nodes
==================================
This template can be used for nodes that are started from Ubuntu
cloud images (or CentOS, with necessary changes in cloudinit_user_data).
The following set of cloudinit_user_data and cloudinit_meta_data is an
example, change it for your purposes.
.. code-block:: yaml
---
aliases:
dynamic_addresses_pool:
- &pool_default !os_env POOL_DEFAULT, 10.10.0.0/16:24
default_interface_model:
- &interface_model !os_env INTERFACE_MODEL, e1000
template:
devops_settings:
env_name: !os_env ENV_NAME
address_pools:
public-pool01:
net: *pool_default
params:
vlan_start: 1210
ip_reserved:
gateway: +1
l2_network_device: +1
ip_ranges:
dhcp: [+128, -32]
rack-01: [+2, +127]
private-pool01:
net: *pool_default
storage-pool01:
net: *pool_default
management-pool01:
net: *pool_default
groups:
- name: default
driver:
name: devops.driver.libvirt
params:
connection_string: !os_env CONNECTION_STRING, qemu:///system
storage_pool_name: !os_env STORAGE_POOL_NAME, default
stp: False
hpet: False
use_host_cpu: !os_env DRIVER_USE_HOST_CPU, true
network_pools:
public: public-pool01
private: private-pool01
storage: storage-pool01
management: management-pool01
l2_network_devices:
public:
address_pool: public-pool01
dhcp: true
forward:
mode: nat
storage:
address_pool: storage-pool01
dhcp: false
management:
address_pool: management-pool01
dhcp: false
private:
address_pool: private-pool01
dhcp: false
nodes:
- name: node-1
role: k8s
params: &rack-01-node-params
vcpu: !os_env SLAVE_NODE_CPU, 2
memory: !os_env SLAVE_NODE_MEMORY, 2048
boot:
- hd
cloud_init_volume_name: iso
cloud_init_iface_up: enp0s3
volumes:
- name: system
capacity: !os_env NODE_VOLUME_SIZE, 50
source_image: !os_env CLOUD_IMAGE_PATH # https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img
format: qcow2
- name: iso # Volume with name 'iso' will be used
# for store image with cloud-init metadata.
capacity: 1
format: raw
device: cdrom
bus: ide
cloudinit_meta_data: |
# All the data below will be stored as a string object
instance-id: iid-local1
local-hostname: {hostname}
network-interfaces: |
auto {interface_name}
iface {interface_name} inet static
address {address}
network {network}
netmask {netmask}
gateway {gateway}
dns-nameservers 8.8.8.8
cloudinit_user_data: |
#cloud-config, see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
# All the data below will be stored as a string object
ssh_pwauth: True
users:
- name: vagrant
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
chpasswd:
list: |
vagrant:vagrant
expire: False
bootcmd:
# Block access to SSH while node is preparing
- cloud-init-per once sudo iptables -A INPUT -p tcp --dport 22 -j DROP
runcmd:
# Prepare network connection
- sudo ifup {interface_name}
- sudo route add default gw {gateway} {interface_name}
# Prepare necessary packages on the node
- sudo apt-get update
- sudo apt-get upgrade -y
- sudo apt-get install -y git python-setuptools python-dev python-pip gcc libssl-dev libffi-dev vim software-properties-common
- sudo apt-get autoremove -y
- sudo pip install -U setuptools pip
- sudo pip install 'cryptography>=1.3.2'
- sudo pip install 'cffi>=1.6.0'
# Node is ready, allow SSH access
- sudo iptables -D INPUT -p tcp --dport 22 -j DROP
interfaces:
- label: enp0s3
l2_network_device: public
interface_model: *interface_model
- label: enp0s4
l2_network_device: private
interface_model: *interface_model
- label: enp0s5
l2_network_device: storage
interface_model: *interface_model
- label: enp0s6
l2_network_device: management
interface_model: *interface_model
network_config:
enp0s3:
networks:
- public
enp0s4:
networks:
- private
enp0s5:
networks:
- storage
enp0s6:
networks:
- management
- name: node-2
role: k8s
params: *rack-01-node-params
- name: node-3
role: k8s
params: *rack-01-node-params