import files
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
; DO NOT EDIT (unless you know what you are doing)
|
||||
;
|
||||
; This subdirectory is a git "subrepo", and this file is maintained by the
|
||||
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
|
||||
;
|
||||
[subrepo]
|
||||
remote = ssh://git@github.com/1001Pharmacies/ansible-disks
|
||||
branch = master
|
||||
commit = c0ac6978d715b461fbf20aca719cd5196bc60645
|
||||
parent = d01cccd9bab3a63d60ba251e3719767635ccd5d2
|
||||
method = merge
|
||||
cmdver = 0.4.0
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Wizcorp
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,77 @@
|
||||
Disk
|
||||
====
|
||||
|
||||
This role allows you to format extra disks and attach them to different mount points.
|
||||
|
||||
You can use it to move the data of different services to another disk.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
### Inventory
|
||||
|
||||
Because the configuration for additional disks must be stored using the YAML
|
||||
syntax, you have to write it in a `group_vars` directory.
|
||||
|
||||
```yaml
|
||||
# inventory/group_vars/GROUP_NAME
|
||||
disks_additional_disks:
|
||||
- disk: /dev/sdb
|
||||
fstype: ext4
|
||||
mount_options: defaults
|
||||
mount: /data
|
||||
user: www-data
|
||||
group: www-data
|
||||
disable_periodic_fsck: false
|
||||
- disk: /dev/nvme0n1
|
||||
part: /dev/nvme0n1p1
|
||||
fstype: xfs
|
||||
mount_options: defaults,noatime
|
||||
mount: /data2
|
||||
- device_name: /dev/sdf
|
||||
fstype: ext4
|
||||
mount_options: defaults
|
||||
mount: /data
|
||||
- disk: nfs-host:/nfs/export
|
||||
fstype: nfs
|
||||
mount_options: defaults,noatime
|
||||
mount: /mnt/nfs
|
||||
```
|
||||
|
||||
* `disk` is the device, you want to mount.
|
||||
* `part` is the first partition name. If not specified, `1` will be appended to the disk name.
|
||||
* `fstype` allows you to choose the filesystem to use with the new disk.
|
||||
* `mount_options` allows you to specify custom mount options.
|
||||
* `mount` is the directory where the new disk should be mounted.
|
||||
* `user` sets owner of the mount directory (default: `root`).
|
||||
* `group` sets group of the mount directory (default: `root`).
|
||||
* `disable_periodic_fsck` deactivates the periodic ext3/4 filesystem check for the new disk.
|
||||
|
||||
You can add:
|
||||
* `disks_package_use` is the required package manager module to use (yum, apt, etc). The default 'auto' will use existing facts or try to autodetect it.
|
||||
|
||||
The following filesystems are currently supported:
|
||||
- [btrfs](http://en.wikipedia.org/wiki/BTRFS) *
|
||||
- [ext2](http://en.wikipedia.org/wiki/Ext2)
|
||||
- [ext3](http://en.wikipedia.org/wiki/Ext3)
|
||||
- [ext4](http://en.wikipedia.org/wiki/Ext4)
|
||||
- [nfs](http://en.wikipedia.org/wiki/Network_File_System) *
|
||||
- [xfs](http://en.wikipedia.org/wiki/XFS) *
|
||||
|
||||
*) Note: To use these filesystems you have to define and install additional software packages. Please estimate the right package names for your operating system.
|
||||
|
||||
```yaml
|
||||
# inventory/group_vars/GROUP_NAME
|
||||
disks_additional_packages:
|
||||
- xfsprogs # package for mkfs.xfs on RedHat / Ubuntu
|
||||
- btrfs-progs # package for mkfs.btrfs on CentOS / Debian
|
||||
disks_additional_services:
|
||||
- rpc.statd # start rpc.statd service for nfs
|
||||
```
|
||||
|
||||
How it works
|
||||
------------
|
||||
|
||||
It uses `sfdisk` to partition the disk with a single primary partition spanning the entire disk.
|
||||
The specified filesystem will then be created with `mkfs`.
|
||||
Finally the new partition will be mounted to the specified mount path.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
# Aditional disks that need to be formated and mounted.
|
||||
# See README for syntax and usage.
|
||||
disks_additional_disks: []
|
||||
disks_additional_packages: []
|
||||
disks_additional_services: []
|
||||
disks_discover_aws_nvme_ebs: False
|
||||
disks_package_use: auto
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
# file: handlers/main.yml
|
||||
|
||||
- name: restart services
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_additional_disks_handler_notify.results }}'
|
||||
service:
|
||||
name: "{{item.0.service}}"
|
||||
state: restarted
|
||||
when: item.1.changed and item.0.service is defined
|
||||
|
||||
- name: restart services - nfs
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_additional_disks_nfs_handler_notify.results }}'
|
||||
service:
|
||||
name: "{{item.0.service}}"
|
||||
state: restarted
|
||||
when: item.1.changed and item.0.service is defined
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from ctypes import *
|
||||
from fcntl import ioctl
|
||||
from pathlib import Path
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
|
||||
module = AnsibleModule(argument_spec=dict(
|
||||
config=dict(required=True, type='list'),
|
||||
))
|
||||
|
||||
|
||||
NVME_ADMIN_IDENTIFY = 0x06
|
||||
NVME_IOCTL_ADMIN_CMD = 0xC0484E41
|
||||
AMZN_NVME_VID = 0x1D0F
|
||||
AMZN_NVME_EBS_MN = "Amazon Elastic Block Store"
|
||||
|
||||
class nvme_admin_command(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [("opcode", c_uint8), # op code
|
||||
("flags", c_uint8), # fused operation
|
||||
("cid", c_uint16), # command id
|
||||
("nsid", c_uint32), # namespace id
|
||||
("reserved0", c_uint64),
|
||||
("mptr", c_uint64), # metadata pointer
|
||||
("addr", c_uint64), # data pointer
|
||||
("mlen", c_uint32), # metadata length
|
||||
("alen", c_uint32), # data length
|
||||
("cdw10", c_uint32),
|
||||
("cdw11", c_uint32),
|
||||
("cdw12", c_uint32),
|
||||
("cdw13", c_uint32),
|
||||
("cdw14", c_uint32),
|
||||
("cdw15", c_uint32),
|
||||
("reserved1", c_uint64)]
|
||||
|
||||
class nvme_identify_controller_amzn_vs(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [("bdev", c_char * 32), # block device name
|
||||
("reserved0", c_char * (1024 - 32))]
|
||||
|
||||
class nvme_identify_controller_psd(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [("mp", c_uint16), # maximum power
|
||||
("reserved0", c_uint16),
|
||||
("enlat", c_uint32), # entry latency
|
||||
("exlat", c_uint32), # exit latency
|
||||
("rrt", c_uint8), # relative read throughput
|
||||
("rrl", c_uint8), # relative read latency
|
||||
("rwt", c_uint8), # relative write throughput
|
||||
("rwl", c_uint8), # relative write latency
|
||||
("reserved1", c_char * 16)]
|
||||
|
||||
class nvme_identify_controller(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [("vid", c_uint16), # PCI Vendor ID
|
||||
("ssvid", c_uint16), # PCI Subsystem Vendor ID
|
||||
("sn", c_char * 20), # Serial Number
|
||||
("mn", c_char * 40), # Module Number
|
||||
("fr", c_char * 8), # Firmware Revision
|
||||
("rab", c_uint8), # Recommend Arbitration Burst
|
||||
("ieee", c_uint8 * 3), # IEEE OUI Identifier
|
||||
("mic", c_uint8), # Multi-Interface Capabilities
|
||||
("mdts", c_uint8), # Maximum Data Transfer Size
|
||||
("reserved0", c_uint8 * (256 - 78)),
|
||||
("oacs", c_uint16), # Optional Admin Command Support
|
||||
("acl", c_uint8), # Abort Command Limit
|
||||
("aerl", c_uint8), # Asynchronous Event Request Limit
|
||||
("frmw", c_uint8), # Firmware Updates
|
||||
("lpa", c_uint8), # Log Page Attributes
|
||||
("elpe", c_uint8), # Error Log Page Entries
|
||||
("npss", c_uint8), # Number of Power States Support
|
||||
("avscc", c_uint8), # Admin Vendor Specific Command Configuration
|
||||
("reserved1", c_uint8 * (512 - 265)),
|
||||
("sqes", c_uint8), # Submission Queue Entry Size
|
||||
("cqes", c_uint8), # Completion Queue Entry Size
|
||||
("reserved2", c_uint16),
|
||||
("nn", c_uint32), # Number of Namespaces
|
||||
("oncs", c_uint16), # Optional NVM Command Support
|
||||
("fuses", c_uint16), # Fused Operation Support
|
||||
("fna", c_uint8), # Format NVM Attributes
|
||||
("vwc", c_uint8), # Volatile Write Cache
|
||||
("awun", c_uint16), # Atomic Write Unit Normal
|
||||
("awupf", c_uint16), # Atomic Write Unit Power Fail
|
||||
("nvscc", c_uint8), # NVM Vendor Specific Command Configuration
|
||||
("reserved3", c_uint8 * (704 - 531)),
|
||||
("reserved4", c_uint8 * (2048 - 704)),
|
||||
("psd", nvme_identify_controller_psd * 32), # Power State Descriptor
|
||||
("vs", nvme_identify_controller_amzn_vs)] # Vendor Specific
|
||||
|
||||
class ebs_nvme_device:
|
||||
def __init__(self, device):
|
||||
self.device = device
|
||||
self.ctrl_identify()
|
||||
|
||||
def _nvme_ioctl(self, id_response, id_len):
|
||||
admin_cmd = nvme_admin_command(opcode = NVME_ADMIN_IDENTIFY,
|
||||
addr = id_response,
|
||||
alen = id_len,
|
||||
cdw10 = 1)
|
||||
|
||||
with open(self.device, "w") as nvme:
|
||||
ioctl(nvme, NVME_IOCTL_ADMIN_CMD, admin_cmd)
|
||||
|
||||
def ctrl_identify(self):
|
||||
self.id_ctrl = nvme_identify_controller()
|
||||
self._nvme_ioctl(addressof(self.id_ctrl), sizeof(self.id_ctrl))
|
||||
|
||||
def is_ebs(self):
|
||||
if self.id_ctrl.vid != AMZN_NVME_VID:
|
||||
return False
|
||||
if self.id_ctrl.mn.strip() != AMZN_NVME_EBS_MN:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_volume_id(self):
|
||||
vol = self.id_ctrl.sn.decode('utf-8')
|
||||
|
||||
if vol.startswith("vol") and vol[3] != "-":
|
||||
vol = "vol-" + vol[3:]
|
||||
|
||||
return vol.strip()
|
||||
|
||||
def get_block_device(self, stripped=False):
|
||||
dev = self.id_ctrl.vs.bdev.decode('utf-8')
|
||||
|
||||
if stripped and dev.startswith("/dev/"):
|
||||
dev = dev[5:]
|
||||
|
||||
return dev.strip()
|
||||
|
||||
|
||||
def update_disk(disk, mapping):
|
||||
if 'device_name' not in disk:
|
||||
return disk
|
||||
|
||||
device_name = disk['device_name'][5:]
|
||||
if device_name not in mapping:
|
||||
return disk
|
||||
|
||||
volume_id = mapping[device_name]
|
||||
link_path = '/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol%s' % volume_id[4:]
|
||||
resolved = str(Path(link_path).resolve())
|
||||
|
||||
new_disk = dict(disk)
|
||||
new_disk['disk'] = resolved
|
||||
new_disk['part'] = '%sp1' % resolved
|
||||
return new_disk
|
||||
|
||||
|
||||
def main():
|
||||
src_config = module.params['config']
|
||||
|
||||
lsblkOutput = subprocess.check_output(['lsblk', '-J'])
|
||||
lsblk = json.loads(lsblkOutput.decode('utf-8'))
|
||||
mapping = {}
|
||||
for blockdevice in lsblk['blockdevices']:
|
||||
try:
|
||||
dev = ebs_nvme_device('/dev/%s' % blockdevice['name'])
|
||||
except OSError:
|
||||
continue
|
||||
except IOError:
|
||||
continue
|
||||
if dev.is_ebs():
|
||||
continue
|
||||
mapping[dev.get_block_device()] = dev.get_volume_id()
|
||||
|
||||
new_config = [
|
||||
update_disk(disk, mapping) for disk in src_config
|
||||
]
|
||||
|
||||
facts = {'blockDeviceMapping': mapping, 'config': new_config, 'source_config': src_config}
|
||||
result = {"changed": False, "ansible_facts": facts}
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,21 @@
|
||||
galaxy_info:
|
||||
author: Emilien Kenler <ekenler@wizcorp.jp>
|
||||
description: This role allows setting up extra disks and their mount points
|
||||
company: Wizcorp K.K.
|
||||
license: MIT
|
||||
min_ansible_version: 2.0.0
|
||||
platforms:
|
||||
- name: EL
|
||||
versions:
|
||||
- 6
|
||||
- 7
|
||||
- name: Debian
|
||||
versions:
|
||||
- wheezy
|
||||
- jessie
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- all
|
||||
categories:
|
||||
- system
|
||||
dependencies: []
|
||||
@@ -0,0 +1,173 @@
|
||||
- name: 'Install Python PIP'
|
||||
package: >
|
||||
name=py3-pip
|
||||
state=present
|
||||
when: ansible_os_family|lower == "alpine"
|
||||
|
||||
- name: 'Install Python PIP'
|
||||
package: >
|
||||
name=python-pip
|
||||
state=present
|
||||
when: ansible_os_family|lower != "alpine"
|
||||
|
||||
- name: 'Install python-pathlib'
|
||||
pip: >
|
||||
name=pathlib
|
||||
state=present
|
||||
|
||||
- name: "Discover NVMe EBS"
|
||||
disks_ebs_config:
|
||||
config: "{{ disks_additional_disks }}"
|
||||
register: __disks_ebs_config
|
||||
when: disks_discover_aws_nvme_ebs | default(True) | bool
|
||||
|
||||
- set_fact:
|
||||
disks_additional_disks: "{{ disks_additional_disks|defaut([]) + __disks_ebs_config['ansible_facts']['config'] }}"
|
||||
when: __disks_ebs_config is defined and 'ansible_facts' in __disks_ebs_config
|
||||
|
||||
- name: "Install parted"
|
||||
package:
|
||||
name: parted
|
||||
state: present
|
||||
use: '{{ disks_package_use }}'
|
||||
when: disks_additional_disks
|
||||
tags: ['disks', 'pkgs']
|
||||
|
||||
- name: "Install additional fs progs"
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items: "{{ disks_additional_packages|default([]) }}"
|
||||
when: disks_additional_packages is defined
|
||||
tags: ['disks', 'pkgs']
|
||||
|
||||
- name: disks - start additional services
|
||||
service:
|
||||
name: "{{item}}"
|
||||
enabled: yes
|
||||
state: started
|
||||
with_items: "{{ disks_additional_services|default([]) }}"
|
||||
tags: ['disks', 'pkgs']
|
||||
|
||||
- name: "Get disk alignment for disks"
|
||||
shell: |
|
||||
if
|
||||
[[ -e /sys/block/{{ item.disk | basename }}/queue/optimal_io_size && -e /sys/block/{{ item.disk | basename }}/alignment_offset && -e /sys/block/{{ item.disk | basename }}/queue/physical_block_size ]];
|
||||
then
|
||||
echo $[$(( ($(cat /sys/block/{{ item.disk | basename }}/queue/optimal_io_size) + $(cat /sys/block/{{ item.disk | basename }}/alignment_offset)) / $(cat /sys/block/{{ item.disk | basename }}/queue/physical_block_size) )) | 2048];
|
||||
else
|
||||
echo 2048;
|
||||
fi
|
||||
args:
|
||||
creates: '{{ item.part | default(item.disk + "1") }}'
|
||||
executable: '/bin/bash'
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
register: disks_offset
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Ensure the disk exists"
|
||||
stat:
|
||||
path: '{{ item.disk }}'
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
register: disks_stat
|
||||
changed_when: False
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Partition additional disks"
|
||||
shell: |
|
||||
if
|
||||
[ -b {{ item.disk }} ]
|
||||
then
|
||||
[ -b {{ item.part | default(item.disk + "1") }} ] || parted -a optimal --script "{{ item.disk }}" mklabel gpt mkpart primary {{ disks_offset.stdout|default("2048") }}s 100% && sleep 5 && partprobe {{ item.disk }}; sleep 5
|
||||
fi
|
||||
args:
|
||||
creates: '{{ item.part | default(item.disk + "1") }}'
|
||||
executable: '/bin/bash'
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Create filesystem on the first partition"
|
||||
filesystem:
|
||||
dev: '{{ item.0.part | default(item.0.disk + "1") }}'
|
||||
force: '{{ item.0.force|d(omit) }}'
|
||||
fstype: '{{ item.0.fstype }}'
|
||||
opts: '{{ item.0.fsopts|d(omit) }}'
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_stat.results }}'
|
||||
when: item.1.stat.exists
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Disable periodic fsck and reserved space on ext3 or ext4 formatted disks"
|
||||
environment:
|
||||
PATH: "{{ ansible_env.PATH }}:/usr/sbin:/sbin"
|
||||
shell: tune2fs -c0 -i0 -m0 {{ item.0.part | default(item.0.disk + "1") }}
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_stat.results }}'
|
||||
when: "disks_additional_disks and ( item.0.fstype == 'ext4' or item.0.fstype == 'ext3' ) and item.0.disable_periodic_fsck|default(false)|bool and item.1.stat.exists"
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Ensure the mount directory exists"
|
||||
file:
|
||||
path: '{{ item.mount }}'
|
||||
state: directory
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Get UUID for partition"
|
||||
environment:
|
||||
PATH: "{{ ansible_env.PATH }}:/usr/sbin:/sbin"
|
||||
command: blkid -s UUID -o value {{ item.0.part | default(item.0.disk + "1") }}
|
||||
check_mode: no
|
||||
register: disks_blkid
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_stat.results }}'
|
||||
changed_when: False
|
||||
when: item.1.stat.exists
|
||||
tags: ['disks']
|
||||
|
||||
- name: "Mount additional disks"
|
||||
mount:
|
||||
name: '{{ item.0.mount }}'
|
||||
fstype: '{{ item.0.fstype }}'
|
||||
opts: '{{ item.0.mount_options|d(omit) }}'
|
||||
passno: '0'
|
||||
src: 'UUID={{ item.1.stdout }}'
|
||||
state: '{{ item.0.mount_state|d("mounted") }}'
|
||||
with_together:
|
||||
- '{{ disks_additional_disks }}'
|
||||
- '{{ disks_blkid.results }}'
|
||||
- '{{ disks_stat.results }}'
|
||||
when: item.2.stat.exists
|
||||
tags: ['disks']
|
||||
register: disks_additional_disks_handler_notify
|
||||
notify:
|
||||
- restart services
|
||||
|
||||
- name: "Mount additional disks - nfs"
|
||||
mount:
|
||||
name: '{{ item.mount }}'
|
||||
fstype: '{{ item.fstype }}'
|
||||
opts: '{{ item.mount_options|d(omit) }}'
|
||||
src: '{{ item.disk }}'
|
||||
state: '{{ item.mount_state|d("mounted") }}'
|
||||
when: item.fstype == 'nfs'
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
tags: ['disks']
|
||||
register: disks_additional_disks_nfs_handler_notify
|
||||
notify:
|
||||
- restart services - nfs
|
||||
|
||||
- name: "Ensure the permissions are set correctly"
|
||||
file:
|
||||
path: '{{ item.mount }}'
|
||||
owner: '{{ item.user | default("root") }}'
|
||||
group: '{{ item.group | default("root") }}'
|
||||
state: directory
|
||||
with_items: '{{ disks_additional_disks }}'
|
||||
when: item.user is defined or item.group is defined
|
||||
tags: ['disk']
|
||||
|
||||
- meta: flush_handlers
|
||||
Reference in New Issue
Block a user