Page_c76a3fcf https://linux-notes.org Unix/ Linux блог, на котором можно найти полезную информацию по настройке ОС и ПО. Wed, 17 Nov 2021 14:12:25 +0000 ru-RU hourly 1 https://wordpress.org/?v=6.7.2 Page_c76a3fcf https://linux-notes.org/pull-push-aws-ecr-obrazov-cherez-aws-route53-cname/ https://linux-notes.org/pull-push-aws-ecr-obrazov-cherez-aws-route53-cname/#comments Wed, 17 Nov 2021 14:12:22 +0000 https://linux-notes.org/?p=18186 можно сделать CNAME в AWS Route53 на уже созданный AWS ECR репозиторий, но не тут то было.

The post Pull/Push AWS ECR образов через AWS Route53 CNAME first appeared on linux-notes.org.]]>

На днях, появилась задача — выполнить миграцию docker-registry в AWS ECR и сделать нормальную URL для AWS ECRб чтобы можно было работать и запомнить название домена легко. Сразу упала мысля о том, что можно сделать CNAME в AWS Route53 на уже созданный AWS ECR репозиторий, но не тут то было.

Создаю AWS Route53 CNAME, что-то типа такого:

docker-ecr.internal.vnatarov.io -> XXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com

Выглядит это так:

Если вы выполните авторизацию по созданному CNAME, то получите ошибку:

$ CNAME_URL="docker-ecr.internal.vnatarov.io" &&\
aws ecr get-login-password --region us-east-1 --profile default | docker login --username AWS --password-stdin $CNAME_URL
Error response from daemon: Get "https://docker-ecr.internal.vnatarov.io/v2/": x509: certificate is valid for *.dkr.ecr.us-east-1.amazonaws.com, *.dkr.ecr.us-east-1.vpce.amazonaws.com, not docker-ecr.internal.vnatarov.io

Т.е нельзя использовать CNAME т.к Амазон подписал AWS ECR домен своим сертификатом. Окай…. Не логично, но ладно! Я думал все у Амазан будет интуитивно легко и просто — нет.

Подумав немного, придумал пару решений:

  • Написать свою обретку при работе с AWS ECR.
  • Использовать proxy.
  • Попробовать использовать Amazon ECR interface VPC endpoints (AWS PrivateLink).

Приступим!

Pull/Push AWS ECR образов через AWS Route53 CNAME

Покопавшись в REST API Amazon (boto3), я увидел что можно подложить хеадеры и выполнить авторизацию и получить репозитории с AWS ECR. Сразу решил проверить, например вот так:

$ CNAME_URL="https://docker-ecr.internal.vnatarov.io" &&\
AWS_ACCOUNT_ID="XXXXXXXXXXXXXXX" &&\
AWS_ECR_REPO_NAME="repo-name" &&\
TOKEN=$(aws ecr get-authorization-token --region us-east-1 --profile default --output text --query 'authorizationData[].authorizationToken') && curl -k -i -H "Host: $AWS_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com" -H "Authorization: Basic $TOKEN" -H "X-Forwarded-Proto: https" -H "X-Forwarded-For 127.0.0.1" -H "X-Real-IP: 93.72.109.140" $CNAME_URL/v2/$AWS_ECR_REPO_NAME/tags/list

Вывод команды:

HTTP/1.1 200 OK
Docker-Distribution-Api-Version: registry/2.0
Link: <https://XXXXXXXXXXXXXXX.dkr.ecr.us-east-1.amazonaws.com/v2/repo-name/tags/list?last=ukD72mdD%2FmC8b5xV3susmJzzaTgp3hKwR9nRUW1yZZ7uIM42T5Qni9DYGx4CJpnhNeJ6HnjEzAukCdhHEgsIYR6ZPbIPWoMte%2Bkmss2BWER%2BOoldEmVa6n9tG88nfx8J3qz2X7nNmzKTvj9S75hqjlupun8iXyGm2Cef6EHKr6JqI7jXVAS0aBWToKUCsATn1R2LRKTxsdsk7HLTSmjieo3kdVioZ6%2F0%2BsiGagwFc6QZfrwH1%2Bl%2F%2Ba1ritf4IwZUXVC4kuID%2BzHKxz9rtgMBgDDBA1yjW8hZqO3K2tOa77h3i%2BPVqj6aHA096YMRh5BFiiLWPGgss0L4QQahaooOLRRg7kdr5k%2FZIqllcmGaLbioNLy3R5jOon7X61YbIGF7fUOkssj72o37fpPd%2FJG2g%3D%3D>; rel="next"
Date: Wed, 17 Nov 2021 11:06:47 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked

{"name":"repo-name","tags":["943bf60acceb327ee85717d2b52cfd97374f4340","ade31de1df53b7c96575f6f27bec17c9f7702cb8"]}

С вывода команды можно понять что подложив хеадеры, можно получить репы. Пишем питон-скрипт…. Потребовалось часов 12 на написания данного скрипта и он довольно стабилен в работе.

Установкой Питон и pip я не буду. Но если нет в систему — ставим. А тем временем, нужно установить следующие пакеты:

# A Fast, Extensible Progress Bar for Python and CLI
tqdm==4.62.3

# AWS
boto3~=1.19.3
botocore~=1.22.3

# Others
setuptools~=57.4.0
urllib3~=1.22

Python скрипт выглядит:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import json
import logging
import os
import shutil
import tarfile
import time
import urllib
from tempfile import mkdtemp

import boto3
import botocore
import botocore.session
import urllib3
from botocore.config import Config

# Initialize Logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logging.captureWarnings(True)

CACHE_DIR_ROOT = os.path.expanduser("~")
assert os.path.isdir(CACHE_DIR_ROOT)
CACHE_DIR = CACHE_DIR_ROOT + '/.docker-pull-layers-cache/'

if not os.path.exists(CACHE_DIR):
    print("Creating cache directory: " + CACHE_DIR)
    os.makedirs(CACHE_DIR)


def ec2_connector(aws_settings):
    if (aws_settings['client'] is not None) and (aws_settings['region'] is not None):
        try:
            session = botocore.session.get_session()
            access_key = session.get_credentials().access_key
            secret_key = session.get_credentials().secret_key
            session_token = session.get_credentials().token

            session = boto3.session.Session()
            ec2 = session.client(aws_access_key_id=access_key,
                                 aws_secret_access_key=secret_key,
                                 aws_session_token=session_token,
                                 service_name=aws_settings['client'],
                                 region_name=aws_settings['region'],
                                 config=Config(retries={'max_attempts': 3})
                                 )
            return ec2
        except Exception as err:
            print("Failed to create a boto3 client connection to ecr:\n", str(err))
            logger.error('ERROR: Failed to create a boto3 client connection')
            return False
    else:
        print('Please use/set [--bclient] and [--region]')
        return False


def ecr_connector(aws_settings):
    if (aws_settings['role_name'] is None or aws_settings['role_name'] == "None") \
            and (aws_settings['role_session'] is None or aws_settings['role_session'] == "None"):
        try:
            session = boto3.session.Session(profile_name=aws_settings['profile_name'])
            # Will retry any method call at most 3 time(s)
            ecr = session.client(service_name=aws_settings['client'],
                                 region_name=aws_settings['region'],
                                 config=Config(retries={'max_attempts': 3})
                                 )
            return ecr
        except Exception as err:
            print("Failed to create a boto3 client connection to ecr:\n", str(err))
            logger.error('ERROR: Failed to create a boto3 client connection to ecr')
            return False
    elif (aws_settings['profile_name'] is None or aws_settings['profile_name'] == "None") \
            and (aws_settings['role_name'] is not None or aws_settings['role_name'] != "None") \
            and (aws_settings['role_session'] is not None or aws_settings['role_session'] != "None"):
        try:
            session = boto3.session.Session()
            sts = session.client(service_name='sts',
                                 region_name=aws_settings['region'],
                                 config=Config(retries={'max_attempts': 3})
                                 )

            assumed_role_object = sts.assume_role(
                RoleArn="{0}".format(aws_settings['role_name']),
                RoleSessionName='{0}'.format(aws_settings['role_session'])
            )
            # can be used ay name, but need to add restriction for the name!
            ecr = session.client(aws_access_key_id=assumed_role_object['Credentials']['AccessKeyId'],
                                 aws_secret_access_key=assumed_role_object['Credentials']['SecretAccessKey'],
                                 aws_session_token=assumed_role_object['Credentials']['SessionToken'],
                                 service_name=aws_settings['client'],
                                 region_name=aws_settings['region'],
                                 config=Config(retries={'max_attempts': 3})
                                 )

            return ecr
        except Exception as err:
            print("Failed to create a boto3 client connection to ecr:\n", str(err))
            logger.error('ERROR: Failed to create a boto3 client connection to ecr')
            return False
    else:
        print('Please use/set [--profile-name] or [--role-name] with [--role-session]')
        return False


def get_ecr_repos(aws_settings):
    ecr = ecr_connector(aws_settings)

    if ecr:
        try:
            repos = ecr.describe_repositories()
            print(repos)

            print("The repos:\n {}!".format(repos))
        except botocore.exceptions.ClientError as err:
            error_code = str(err)
            logger.error('ERROR: {0}. Forbidden Access!'.format(error_code))
    else:
        exit(-1)

    return get_ecr_repos


def get_ecr_repo(aws_settings, ecr_repo):
    ecr_repo_status = False

    ecr = ecr_connector(aws_settings)

    if ecr:
        try:
            repo = ecr.describe_repositories(repositoryNames=[ecr_repo])
            # print("A repo {} is already exists!".format(ecr_repo))
            ecr_repo_status = repo
            return ecr_repo_status
        except botocore.exceptions.ClientError as err:
            error_code = str(err)
            logger.error('ERROR: {0}. Forbidden Access!'.format(error_code))
            ecr_repo_status = False
            return ecr_repo_status
    else:
        exit(-1)

    return ecr_repo_status


def get_authorization_token(aws_settings, ecr_repo):
    global auth_token

    ecr = ecr_connector(aws_settings)

    ecr_repo = get_ecr_repo(aws_settings, ecr_repo)
    ecr_repo_id = ecr_repo['repositories'][0]['registryId']
    if ecr:
        response = ecr.get_authorization_token(registryIds=[ecr_repo_id])
        if response['ResponseMetadata']['HTTPStatusCode'] == 200:
            auth_token = response['authorizationData'][0]['authorizationToken']
            # print("Authorization token: ", auth_token)
        elif response['ResponseMetadata']['HTTPStatusCode'] == 401:
            print("You're not authorized")
            exit(1)
    else:
        exit(-1)

    return auth_token


def http_request(method='GET', url='', h=None, retries=False, timeout=30):
    if h is None:
        h = {}
    http = urllib3.PoolManager()

    response = http.request(method=method,
                            url=url,
                            headers=h,
                            retries=retries,
                            timeout=timeout)
    if response.status == 200:
        # print("response: ", response.data.decode('utf-8'))
        logger.info('INFO: Successfully.....')
        return response
    elif response.status == 307:
        # logger.error('HTTP 307 Temporary Redirect redirect status response')
        return response
    elif response.status == 401:
        logger.error('ERROR: Please authorize, the issue: \n\t {}'.format(response.data.decode('utf-8')))
        return response
    elif response.status == 404:
        logger.error('ERROR: 404 page not found')
        return response
    else:
        logger.error('FAILURE: Got an error: \n\t {}'.format(response.data.decode('utf-8')))

        return response


def urllib_request_urlopen(url):
    response = urllib.request.urlopen(url).read()

    return response


def downloading_layer(cache_dir=CACHE_DIR, layer_url=None, out_path='/', h=None):
    """
    Get a layer in a compressed format, and saves it locally (unzipped).
    The tar name is expected to contain a hash, thus to be cacheable.
    """

    if h is None:
        h = {}

    cache_name = cache_dir + layer_url.split("/")[6].replace(':', '_')
    repo_name = layer_url.split("/")[4]

    response_headers = {}
    if not os.path.exists(cache_name):
        response = http_request(method='GET', url=layer_url, h=h, retries=False, timeout=30)
        for key, val in response.headers.iteritems():
            d = {key: val}
            response_headers.update(d)

        layer_location = response_headers['Location']
        layer_location_data = urllib_request_urlopen(layer_location)

        with open(cache_name, mode='wb') as localfile:
            localfile.write(layer_location_data)
            shutil.move(cache_name, cache_name)

    os.makedirs(out_path[:out_path.rfind("/")], exist_ok=True)
    shutil.copyfile(cache_name, out_path)

    return downloading_layer


def ecr_pull(aws_settings, cname_url, ecr_repo, ecr_tag="latest", h=None):
    if h is None:
        h = {}

    ecr = ecr_connector(aws_settings)

    if ecr:
        try:
            manifests_url = cname_url + '/v2/{0}/manifests/{1}'.format(ecr_repo, ecr_tag)
            response = http_request('GET', manifests_url, h)
            web_manifest = json.loads(response.data.decode('utf-8'))
            config_digest = web_manifest['config']['digest']

            # ------------------------------------------------------------------------
            config = cname_url + '/v2/{0}/blobs/{1}'.format(ecr_repo, config_digest)
            response = http_request('GET', config, h)

            response_headers = {}
            for key, val in response.headers.iteritems():
                d = {key: val}
                response_headers.update(d)

            config_location = response_headers['Location']
            config_location_file = urllib_request_urlopen(config_location).decode('utf-8')

            config_filename = config_digest.split(':')[1] + '.json'
            with open(temp_dir + '/' + config_filename, 'w') as outfile:
                json.dump(json.loads(config_location_file), outfile)

            # ------------------------------------------------------------------------
            layer_path_l = []
            for layer in web_manifest['layers']:
                layer_url = cname_url + '/v2/{0}/blobs/{1}'.format(ecr_repo, layer['digest'])
                path = layer['digest'].split(':')[-1] + "/layer.tar"
                out_path = temp_dir + '/' + path

                downloading_layer(cache_dir=CACHE_DIR, layer_url=layer_url, out_path=out_path, h=h)
                layer_path_l.append(path)

            manifest = [{"Config": config_filename, "RepoTags": [], "Layers": layer_path_l}]
            print("config_filename: ", config_filename)
            print("manifest: ", manifest)
            with open(temp_dir + '/' + 'manifest.json', 'w') as outfile:
                json.dump(manifest, outfile)

            with tarfile.open(ecr_tag, "w") as tar_out:
                os.chdir(temp_dir)
                tar_out.add(".")

        except Exception as e:
            logger.error('ERROR: {0}'.format(str(e)))
            print(e)
            exit(1)

    else:
        print("Could not get connect to AWS ECR or get needed repository")
        exit(-1)

    return ecr_pull


def ecr_push():
    print("TBD")

    return ecr_push


def ec2_handler(aws_settings, ecr_repo):
    ecr = ec2_connector(aws_settings)

    # TBD

    return ec2_handler


if __name__ == '__main__':
    start__time = time.time()
    parser = argparse.ArgumentParser(prog='python3 script_name.py -h',
                                     usage='python3 script_name.py {ARGS}',
                                     add_help=True,
                                     prefix_chars='--/',
                                     epilog='''created by Vitalii Natarov'''
                                     )
    parser.add_argument('--version', action='version', version='v0.2.0')
    parser.add_argument('--bclient', dest='boto3_client', help='Set boto3 client', default='ecr')
    parser.add_argument('--region', dest='region', help='Set AWS region for boto3', default='us-east-1')
    parser.add_argument('--pname', '--profile', dest='profile_name', help='Set profile name of AWS',
                        default=None)
    parser.add_argument('--rname', '--role-name', dest='role_name', help='Set role ARN name',
                        default=None)
    parser.add_argument('--rsession', '--role-session', dest='role_session', help='Set role session name',
                        default=None)
    parser.add_argument('--registry-url', '--url', dest='registry_url', help='Set Registry URL',
                        default="https://docker-ecr.internal.logz.io")
    parser.add_argument('--ecr-url', dest='ecr_url', help='Set URL from ECR registry',
                        default="406095609952.dkr.ecr.us-east-1.amazonaws.com")
    parser.add_argument('--ecr-repo', '-repo', dest='ecr_repo', help='Set ECR repo name',
                        default="accounts")
    parser.add_argument('--ecr-repo-tag', '-repo-tag', dest='ecr_repo_tag', help='Set ECR repo tag',
                        default="8f2b55cc45894247d880f7c4ac2042ed7c398dfa")
    parser.add_argument('--ec2', dest='aws_ec2', help='Set ec2 usage', default='False')

    results = parser.parse_args()

    boto3_client = results.boto3_client
    region = results.region
    profile_name = results.profile_name
    role_name = results.role_name
    role_session = results.role_session

    registry_url = results.registry_url
    ecr_url_host = results.ecr_url
    ecr_repo_name = results.ecr_repo
    ecr_repo_tag = results.ecr_repo_tag

    aws_ec2 = results.aws_ec2

    try:
        temp_dir = mkdtemp()

        if aws_ec2 == 'True':
            print("AWS EC2!")
            aws_auth = {
                "client": boto3_client,
                "region": region,
                "ec2_host": 'True'
            }

            ec2_handler(aws_auth)
        else:
            print("Local Usage!")
            aws_auth = {
                "client": boto3_client,
                "region": region,
                "profile_name": profile_name,
                "role_name": role_name,
                "role_session": role_session
            }

            authorization_token = get_authorization_token(aws_auth, ecr_repo_name)
            headers = {
                'Host': str(ecr_url_host),
                'Accept': 'text/plain',
                'X-Forwarded-Proto': 'https',
                'X-Forwarded-For': '127.0.0.1',
                'X-Real-IP': '66.66.66.66',
                'Authorization': 'Basic {}'.format(str(authorization_token))
            }
            ecr_pull(aws_auth, registry_url, ecr_repo_name, ecr_repo_tag, headers)
    finally:
        shutil.rmtree(temp_dir)

    end__time = round(time.time() - start__time, 2)
    print("--- %s seconds ---" % end__time)

Использование — простое:

$ python3 ecr-containers.py --profile=default
config_filename:  2a5d611b6675bbcb15fea6050fdf5af2d974494511967e1ccd816efbe95fa348.json
manifest:  [{'Config': '2a5d611b6675bbcb15fea6050fdf5af2d974494511967e1ccd816efbe95fa348.json', 'RepoTags': [], 'Layers': ['339de151aab4bc06eed8409daae147c408478cb538dacb90cc63f19ad4eba80b/layer.tar', 'c79c496f1543a6cf36a79305de7c709ea55c16b63ea86219c4b98c334b70488d/layer.tar', 'a5382bad4f98c8f742d943e2bd6930626b06af081c57062e06a501d449a4c472/layer.tar', '348e701aca2b0acab9fab3daaa10980e5507b8e55334671f503d1d5f5ef739a5/layer.tar', '965b2a30aa9c5ffea05d772b418c428cde9fa3f85faf37b2b66b719593f4c7b6/layer.tar', '9ee6edf857abe493670c386faa46127a9decea128da3fcafe25015afa8f6b9ef/layer.tar', '75f33f1b8076396b9a688c579ffb6ee4e7d7c83e94a9ba4d15b97a7efb118db7/layer.tar', 'a79e84406bd9b1f9c48e60589691a9ef807c7416cf1b2885eafc95fbbd3bbd8c/layer.tar', 'e5279c73940e27e2d0e32d8c288c32a795eeb12d4bf876e27067a7a49d032d6c/layer.tar', 'b088ed07f0856dbc5d7451536d3379a8c0dee79ddf98f7bdcef98e37549669fa/layer.tar', '09c3905aed6a2dd924c4a3fed0802d3cc191cdfc317c960ea9c43c9f8a4379c5/layer.tar', '7486d5d0bd29e293ed034751b9743c3f30405aa4746f353151ed44e2d827fc76/layer.tar']}]
--- 3.93 seconds ---

Скрипт поддерживает простой хелп, вызвать его можно так:

$ python3 ecr-containers.py -h
usage: python3 script_name.py {ARGS}

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --bclient BOTO3_CLIENT
                        Set boto3 client
  --region REGION       Set AWS region for boto3
  --pname PROFILE_NAME, --profile PROFILE_NAME
                        Set profile name of AWS
  --rname ROLE_NAME, --role-name ROLE_NAME
                        Set role ARN name
  --rsession ROLE_SESSION, --role-session ROLE_SESSION
                        Set role session name
  --registry-url REGISTRY_URL, --url REGISTRY_URL
                        Set Registry URL
  --ecr-url ECR_URL     Set URL from ECR registry
  --ecr-repo ECR_REPO, -repo ECR_REPO
                        Set ECR repo name
  --ecr-repo-tag ECR_REPO_TAG, -repo-tag ECR_REPO_TAG
                        Set ECR repo tag
  --ec2 AWS_EC2         Set ec2 usage

created by Vitalii Natarov

После того как выполнили пуллинг образа, нужно его импортнуть в Докер:

$ docker load < 8f2b55cc45894247d880f7c4ac2042ed7c398dfa
32f366d666a5: Loading layer [==================================================>]  2.801MB/2.801MB
45449966e51a: Loading layer [==================================================>]  192.3MB/192.3MB
83ef90c94f12: Loading layer [==================================================>]   2.92MB/2.92MB
54037642f52c: Loading layer [==================================================>]    200kB/200kB
b9e5e90eb033: Loading layer [==================================================>]  42.09MB/42.09MB
539aca899eb8: Loading layer [==================================================>]     206B/206B
9d00a6a26f53: Loading layer [==================================================>]  4.668kB/4.668kB
bca0da0f1ea4: Loading layer [==================================================>]  1.631kB/1.631kB
cbb7b9b8cd31: Loading layer [==================================================>]  197.9MB/197.9MB
a0f1352db34e: Loading layer [==================================================>]  8.519kB/8.519kB
2543c13d39a0: Loading layer [==================================================>]  1.833kB/1.833kB
eb9876ca8f14: Loading layer [==================================================>]   41.2MB/41.2MB
Loaded image ID: sha256:ada32a4765be57eb1049808ebdbc7b8b6108847375383a21ffe004f3fffc3757

В плнах сделать push для работы с ECR, но это будет немного позже.

Репозиторий можно найти тут:

https://github.com/SebastianUA/ecr-pull-push

У меня все, статья «Pull/Push AWS ECR образов через AWS Route53 CNAME» завершена!

The post Pull/Push AWS ECR образов через AWS Route53 CNAME first appeared on linux-notes.org.]]>
https://linux-notes.org/pull-push-aws-ecr-obrazov-cherez-aws-route53-cname/feed/ 1
Page_c76a3fcf https://linux-notes.org/openpgp-signature-made-by-unknown-entity-v-terraform/ https://linux-notes.org/openpgp-signature-made-by-unknown-entity-v-terraform/#respond Tue, 09 Nov 2021 14:06:11 +0000 https://linux-notes.org/?p=18182 шибка возникает для Azure: 'Error installing provider “azurerm”: openpgp: signature made by unknown entity'.

The post openpgp: signature made by unknown entity в Terraform first appeared on linux-notes.org.]]>

Столкнулся с проблемой ‘Error installing provider “aws”: openpgp: signature made by unknown entity’ и такая ошибка возникает для Azure: ‘Error installing provider “azurerm”: openpgp: signature made by unknown entity’.

Ключ GPG, используемый для подписания и проверки был изменен. Новые выпуски Terraform используют этот обновленный ключ для проверки официальных провайдеров, а официальные выпуски поставщика будут подписаны с помощью этого ключа.

Error installing provider “aws”: openpgp: signature made by unknown entity

Решений будет пару.

Для Terraporm 0.11.x вы можете установить версию AWS на V2.70.0:

provider "aws" {
  region  = "us-east-1"
  version = "v2.70.0"
}

Или, просто при запуске Терраформа, использовать опцию:

$ terraform init -verify-plugins=false

Как-то так.

Error installing provider “azurerm”: openpgp: signature made by unknown entity

Решений будет пару.

Для Terraporm 0.11.x вы можете установить версию Azure на v1.44.0:

provider "azurerm" {
  version = "v1.44.0"
}

Или, просто при запуске Терраформа, использовать опцию:

$ terraform init -verify-plugins=false

Как-то так.

У меня все, статья ‘openpgp: signature made by unknown entity в Terraform’ завершена.

The post openpgp: signature made by unknown entity в Terraform first appeared on linux-notes.org.]]>
https://linux-notes.org/openpgp-signature-made-by-unknown-entity-v-terraform/feed/ 0
Page_c76a3fcf https://linux-notes.org/ustanovka-terraformer-v-unix-linux/ https://linux-notes.org/ustanovka-terraformer-v-unix-linux/#respond Mon, 31 May 2021 15:42:12 +0000 https://linux-notes.org/?p=18093 Terraformer - Инструмент командной строки, который генерирует файлы TF/JSON и TFState на основе существующей инфраструктуры (reverse Terraform).

The post Установка Terraformer в Unix/Linux first appeared on linux-notes.org.]]>

Terraformer — Инструмент командной строки, который генерирует файлы TF/JSON и TFState на основе существующей инфраструктуры (reverse Terraform).

Установка Terraformer в Unix/Linux

Есть пару способов как можно установить данную тулзовину себе на рабоче место.

Установка Terraformer в Linux

Простой способ, выполнить:

$ export PROVIDER={all,google,aws,kubernetes}
curl -LO https://github.com/GoogleCloudPlatform/terraformer/releases/download/$(curl -s https://api.github.com/repos/GoogleCloudPlatform/terraformer/releases/latest | grep tag_name | cut -d '"' -f 4)/terraformer-${PROVIDER}-linux-amd64
chmod +x terraformer-${PROVIDER}-linux-amd64
sudo mv terraformer-${PROVIDER}-linux-amd64 /usr/local/bin/terraformer

Так же, можно собрать пакет, склонировавши репу:

$ git clone https://github.com/GoogleCloudPlatform/terraformer.git

Идем далее.

Установка Terraformer в Mac OS

Один из простых способов — это использовать HOMEBREW:

$ brew install terraformer

Еще можно выполнить:

$ export PROVIDER={all,google,aws,kubernetes}
curl -LO https://github.com/GoogleCloudPlatform/terraformer/releases/download/$(curl -s https://api.github.com/repos/GoogleCloudPlatform/terraformer/releases/latest | grep tag_name | cut -d '"' -f 4)/terraformer-${PROVIDER}-darwin-amd64
chmod +x terraformer-${PROVIDER}-darwin-amd64
sudo mv terraformer-${PROVIDER}-darwin-amd64 /usr/local/bin/terraformer

Так же, можно собрать пакет, склонировавши репу:

$ git clone https://github.com/GoogleCloudPlatform/terraformer.git

Идем далее.

Использование Terraformer в Unix/Linux

Запускаю первый пробный запуск и получаю ошибку:

$ terraformer import aws --resources=vpc,subnet --filter=vpc=default --regions=us-east-1
2021/05/31 17:59:22 aws importing region us-east-1
2021/05/31 17:59:22 open /Users/captain/.terraform.d/plugins/darwin_amd64: no such file or directory

Решением, — есть создание папок:

$ mkdir -p ~/.terraform.d/plugins/darwin_amd64

И пробую сново запустить, получаю:

$ terraformer import aws --resources=vpc,subnet --filter=vpc=default --regions=us-east-1

2021/05/31 18:01:14 aws importing region us-east-1
2021/05/31 18:01:14 fork/exec : no such file or directory

Решением есть, — скопировать бинарь от провайдера (в моем случае — это AWS), например:

$ cp -r Projects/Terraform/aws/examples/s3/.terraform/providers/registry.terraform.io/hashicorp/aws/3.42.0/darwin_amd64/terraform-provider-aws_v3.42.0_x5 ~/.terraform.d/plugins/darwin_amd64/

И запускаем еще раз:

$ terraformer import aws --resources=vpc,subnet --filter=vpc=default --regions=us-east-1
2021/05/31 18:13:30 aws importing region us-east-1
2021/05/31 18:13:33 aws importing... vpc
2021/05/31 18:13:34 aws done importing vpc
2021/05/31 18:13:34 aws importing... subnet
2021/05/31 18:13:35 aws done importing subnet
2021/05/31 18:13:35 Number of resources for service vpc: 0
2021/05/31 18:13:35 Number of resources for service subnet: 12
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-087ab3540e1a1ace4
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-049129ea82abc346a
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-06373f4c58dfe35a9
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-00db3165c99b03ee0
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-07021252bab163bcf
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-02b0ff71a1d00deb2
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-03212d250885f6986
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-04b6d772342323e4f
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-003e3e93d58873d28
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-081b69deddee10c61
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-01b817896302a1b98
2021/05/31 18:13:35 Refreshing state... aws_subnet.tfer--subnet-002D-029a45d1cf920abd6
2021/05/31 18:13:36 Filtered number of resources for service subnet: 12
2021/05/31 18:13:36 Filtered number of resources for service vpc: 0
2021/05/31 18:13:36 aws Connecting....
2021/05/31 18:13:36 aws save vpc
2021/05/31 18:13:36 aws save tfstate for vpc
2021/05/31 18:13:36 aws save subnet
2021/05/31 18:13:36 aws save tfstate for subnet

И так, я сгенерировал стек основываясь на VPC + subnets в определенном регионе. Сам statefile от Терраформа, можно найти по пути:

$ tree generated
generated
`-- aws
    |-- subnet
    |   |-- outputs.tf
    |   |-- provider.tf
    |   |-- subnet.tf
    |   |-- terraform.tfstate
    |   `-- variables.tf
    `-- vpc
        |-- provider.tf
        `-- terraform.tfstate

3 directories, 7 files

Так образом, можно получить всю структуру и при необходимости уменьшить трудозатраты на инвестигейт или рефакторинг существующей структуры

Помощь получить можно выполнив:

$ terraformer --help
Usage:
   [command]

Available Commands:
  help        Help about any command
  import      Import current state to Terraform configuration
  plan        Plan to import current state to Terraform configuration
  version     Print the version number of Terraformer

Flags:
  -h, --help      help for this command
  -v, --version   version for this command

Use " [command] --help" for more information about a command.

Не всегда спасет. Но всегда может выручить!

Статья «Установка Terraformer в Unix/Linux» завершена.

The post Установка Terraformer в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/ustanovka-terraformer-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/sozdaem-github-workflows-s-actions-v-unix-linux/ https://linux-notes.org/sozdaem-github-workflows-s-actions-v-unix-linux/#respond Mon, 19 Oct 2020 17:53:57 +0000 https://linux-notes.org/?p=17735 Недавно, в GitHub появилась возможность использовать workflows для различных задач. Например, можно сделать флов для проверки вашего кода перед тем как код будет смерджен в какую-то из бранчей ( например - master). Я приведу пример использования lint для Terrafrom-а, т.е я хочу чтобы код работал не только у меня локально, но и после того как я его запушу в репозиторий - он будет проходить проверку и даст мне знать если что-то пойдет не так.

The post Создаем GitHub workflows с actions в Unix/Linux first appeared on linux-notes.org.]]>

Недавно, в GitHub появилась возможность использовать workflows для различных задач. Например, можно сделать флов для проверки вашего кода перед тем как код будет смерджен в какую-то из бранчей ( например — master). Я приведу пример использования lint для Terrafrom-а, т.е я хочу чтобы код работал не только у меня локально, но и после того как я его запушу в репозиторий — он будет проходить проверку и даст мне знать если что-то пойдет не так.

Структура моего .github фолдера выглядит так:

$ tree .github | grep -Ev "_BK"
.github
|-- FUNDING.yml
|-- ISSUE_TEMPLATE
|   |-- bug_report.md
|   |-- custom.md
|   `-- feature_request.md
|-- actions
|-- lock.yml
|-- move.yml
|-- no-response.yml
|-- stale.yml
`-- workflows
    |-- terraform-lint.yaml

3 directories, 11 files

Где:

  • FUNDING.yml — Служит файлом для настройки донатинга проекта. Добавил чтобы было…
  • actions — Папка для yaml-файлов с конфигурацией действий для workflows. Т.е описываем что можно выполнять и каким образом. У меня в данном случае — папка пуска и я использую только workflows с готовым примером.
  • workflows — Это папка в которой лежат yaml-файлы с ворк-фловами, т.е файлами конфигураций при работе с вашим проектом. Например, проверка орфограции, выполнение init & plan & apply для Terrafrom-а ( в моем случае). Т.е я хочу сказать — это что-то типа CI/CD в GitHub и похожу что Микромелкие сперли некоторые из фичей с GitLab CI.

Создаем GitHub workflows с actions для Terraform в Unix/Linux

GitHub Actions позволяют создавать собственные рабочие процессы жизненного цикла разработки программного обеспечения (SDLC) непосредственно в вашем репозитории GitHub.

Мой самый простой lint для Terraform выглядит:

$ cat .github/workflows/terraform-lint.yaml
---
name: terraform-lint

on: [push, pull_request]

jobs:
  delivery:
    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@master
      - name: Lint Terraform
        uses: actionshub/terraform-lint@master

Я не создавал экшены и взял готовое использование проекта ( написанного кем-то до меня).

Еще, имеется и другой пример использование:

---
name: 'Terraform GitHub Actions'

# on: 
#   pull_request:
#     branches:
#       - master

on:
  - pull_request

jobs:
  terraform:
    name: 'Terraform'
    runs-on: ubuntu-latest
    steps:
      - name: 'Check out code'
        uses: actions/checkout@master
      - name: 'Terraform Format'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.13
          tf_actions_subcommand: 'fmt'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: 'Terraform Init'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.13
          tf_actions_subcommand: 'init'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          TF_ACTION_WORKING_DIR: '.'
          AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      - name: 'Terraform Validate'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.13
          tf_actions_subcommand: 'validate'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: 'Terraform Plan'
        uses: hashicorp/terraform-github-actions@master
        with:
          tf_actions_version: 0.12.13
          tf_actions_subcommand: 'plan'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          AWS_ACCESS_KEY_ID:  ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY:  ${{ secrets.AWS_SECRET_ACCESS_KEY }}

ЗАМЕЧАНИЕ: В данном примере — использую secrets, которые нужно прописать в настройках самого проекта. Данную настройку можно найти по следующему пути:

<GITHUB_URL>/<YOUR_USER>/<YOUR_REPO_NAME>/settings/secrets

Где:

  • <GITHUB_URL> — URL от гитхаба (с http или https).
  • <YOUR_USER> — Юзер от которого запушился код.
  • <YOUR_REPO_NAME> — Репозиторий.

Например у меня это выглядит так:

https://github.com/SebastianUA/terraform/settings/secrets

Нашел в интернете «runatlantis» проект который позволяет выполнять действия (terraform plan, terraform apply — это как пример) написав команду в комментарии ( например pull request-а). Не проверял работу, но оставил чтобы не забыть про него, если нужно будет заюзать.

Вот и все, статья «Создаем GitHub workflows с actions в Unix/Linux» завершена.

The post Создаем GitHub workflows с actions в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/sozdaem-github-workflows-s-actions-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/can-t-open-pid-file-var-run-nginx-pid-yet-after-start-too-many-levels-of-symbolic-links/ https://linux-notes.org/can-t-open-pid-file-var-run-nginx-pid-yet-after-start-too-many-levels-of-symbolic-links/#comments Wed, 29 Apr 2020 19:42:21 +0000 https://linux-notes.org/?p=17782 Зашел на сервер я такой, думаю - обновлю его ( давно не ставил обновы на него). Выполнил апдейт. Захотел кое-что поменят в конфиге с nginx ( менял то, что не должно было поломать веб-сервер). Но после рестарта службы - веб-сервер лег вообще... Обидненько!

The post Can’t open PID file /var/run/nginx.pid (yet?) after start: Too many levels of symbolic links first appeared on linux-notes.org.]]>

Зашел на сервер я такой, думаю — обновлю его ( давно не ставил обновы на него). Выполнил апдейт. Захотел кое-что поменят в конфиге с nginx ( менял то, что не должно было поломать веб-сервер). Но после рестарта службы — веб-сервер лег вообще… Обидненько!

PS: Если спросить — чего я автоматом не обновляю пакеты или ОС — та не знаю. Чтобы не лягло все! Это ответ на вопрос ( если у кого-то появится).

Ошибка выглядет так:

(captain-notes)─(✗)─(10:08 PM Wed Apr 29)
└─(/home)─(5 files, 28Kb)─> systemctl status nginx.service
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: activating (start) since Wed 2020-04-29 22:06:39 EEST; 1min 54s ago
     Docs: http://nginx.org/en/docs/
   CGroup: /system.slice/nginx.service
           ├─2197 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
           ├─2198 nginx: worker process
           ├─2199 nginx: worker process
           ├─2200 nginx: worker process
           ├─2201 nginx: worker process
           └─2202 nginx: cache manager process

Apr 29 22:06:39 captain-notes systemd[1]: Starting nginx - high performance web server...
Apr 29 22:06:39 captain-notes systemd[1]: Can't open PID file /var/run/nginx.pid (yet?) after start: Too many levels of symbolic links

Приступим к решению!

Can’t open PID file /var/run/nginx.pid (yet?) after start: Too many levels of symbolic links

Приведу пару солюшенов, которые нашел в инете. Перепробовал несколько из них — пофиксил ишью.

Решение №1

Папка /var/run не имеет символической ссылки (symbolic link) для /run директории. Можно проверить это так:

ls -ld /var/run
drwxr-xr-x 12 root root 4096 Apr 29 22:10 /var/run

Так же, папка /run имеется на сервере, проверим:

#  ls -ld /run
drwxr-xr-x 32 root root 980 Apr 29 22:12 /run

Так же, можно грепнуть конфиг nginx:

# grep -ir pid /etc/nginx
/etc/nginx/nginx.conf.default:#pid /var/run/nginx.pid;
/etc/nginx/nginx.conf:#pid /var/run/nginx.pid;

А собственно, решение вот:

# mv /var/run /var/run_orig && ln -s ../run /var/run && ls -ld /var/run

Перезапускаем сервер с nginx и это должно пофиксить проблему.

У меня не решилось… Пробуем другой солюшен….

Решение №2

Смотрим что у нас в конфигах nginx:

# grep -ir pid /etc/nginx
/etc/nginx/nginx.conf:pid        /run/nginx.pid;
/etc/nginx/nginx.conf:#pid /var/run/nginx.pid;

Находим строку:

pid        /var/run/nginx.pid;

И меняем ее на:

pid        /run/nginx.pid;

PS: Можно использовать SED и сделать замену:

# sed -i 's/pid \/var\/run\/nginx.pid/pid \/run\/nginx.pid/' /etc/nginx/nginx.conf

Далее, открываем:

# vim /lib/systemd/system/nginx.service

И меняем строку:

PIDFile=/var/run/nginx.pid

На:

PIDFile=/run/nginx.pid

Чтобы изменения применились, выполняем:

# systemctl daemon-reload

Затем, перезагружаем веб-сервер:

# service nginx restart

В итоге, получаем:

┌(captain-notes)─(✓)─(10:12 PM Wed Apr 29)
└─(/home)─(5 files, 28Kb)─> systemctl status nginx.service
● nginx.service - nginx - high performance web server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2020-04-29 22:12:24 EEST; 10s ago
     Docs: http://nginx.org/en/docs/
  Process: 2996 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
 Main PID: 2997 (nginx)
   CGroup: /system.slice/nginx.service
           ├─2997 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
           ├─2998 nginx: worker process
           ├─2999 nginx: worker process
           ├─3000 nginx: worker process
           ├─3001 nginx: worker process
           ├─3002 nginx: cache manager process
           └─3003 nginx: cache loader process

Apr 29 22:12:24 captain-notes systemd[1]: Starting nginx - high performance web server...
Apr 29 22:12:24 captain-notes systemd[1]: Started nginx - high performance web server.

Я забыл заюзать strace, чтобы отловить ошибку, но нормальное поведение такое:

# strace /run/nginx.pid
execve("/run/nginx.pid", ["/run/nginx.pid"], 0x7ffcaef1ce40 /* 29 vars */) = -1 EACCES (Permission denied)
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=2502, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8def0a3000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2502
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7f8def0a3000, 4096)            = 0
open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.UTF-8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
fstat(2, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8def0a3000
write(2, "strace: exec: Permission denied\n", 32strace: exec: Permission denied
) = 32
exit_group(1)                           = ?
+++ exited with 1 +++

ЗАМЕЧАНИЕ! Данная проблема, может касатья не только nginx, но может затронуть mysql или fail2ban и возможно — другие сервисы.

Вот и все, статья «Can’t open PID file /var/run/nginx.pid (yet?) after start: Too many levels of symbolic links» — завершена!

The post Can’t open PID file /var/run/nginx.pid (yet?) after start: Too many levels of symbolic links first appeared on linux-notes.org.]]>
https://linux-notes.org/can-t-open-pid-file-var-run-nginx-pid-yet-after-start-too-many-levels-of-symbolic-links/feed/ 2
Page_c76a3fcf https://linux-notes.org/aws-lambda-na-python3-v-unix-linux/ https://linux-notes.org/aws-lambda-na-python3-v-unix-linux/#respond Sat, 25 Apr 2020 21:17:10 +0000 https://linux-notes.org/?p=17761 Приведу пример AWS Lambda на python3 которую напишу и расскажу как она будет работать.

The post AWS Lambda на python3 в Unix/Linux first appeared on linux-notes.org.]]>

На работе, недавно попросили написать Lambda для AWS со следующей логикой:

  • CSV файлы, апликейшеном складываются в AWS S3 бакет.
  • Нужно написать python скрипт, который зайдет в бакет через креды и сможет забрать файлы с него.
  • Распарсить полученные CSV файлы с AWS S3 бакета и перевести данные в JSON формат.
  • Данные в JSON формате, отправить в ElasticSearch по определенному индексу.

Вообще, задача — довольно тривиальная и для меня очень простая. Я хорошо знаю python и AWS boto3 чтобы написать данный солюшен.

Не буду много лить воды, по этому сразу приведу код, который выглядит так:

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import argparse
import datetime
import time
import csv
import json
import logging
import urllib3
import os
from gzip import GzipFile
from io import BytesIO, StringIO

import boto3
import botocore
from botocore.config import Config

# Initialize Logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)

"""
Can Override the global variables using Lambda Environment Parameters
"""
globalVars = {}
globalVars['esIndexPrefix'] = "s3-to-es-"
globalVars['esIndexDocType'] = "s3_to_es_docs"


def s3_connector(aws_auth):
    if (aws_auth['role_name'] is None or aws_auth['role_name'] == "None") \
            and (aws_auth['role_session'] is None or aws_auth['role_session'] == "None"):
        try:
            session = boto3.session.Session(profile_name=aws_auth['profile_name'])
            # Will retry any method call at most 3 time(s)
            s3 = session.client(service_name=aws_auth['client'],
                                region_name=aws_auth['region'],
                                config=Config(retries={'max_attempts': 3})
                                )
            return s3
        except Exception as err:
            print("Failed to create a boto3 client connection to S3:\n", str(err))
            logger.error('ERROR: Failed to create a boto3 client connection to S3')
            return False
    elif (aws_auth['profile_name'] is None or aws_auth['profile_name'] == "None") \
            and (aws_auth['role_name'] is not None or aws_auth['role_name'] != "None") \
            and (aws_auth['role_session'] is not None or aws_auth['role_session'] != "None"):
        try:
            session = boto3.session.Session()
            sts = session.client(service_name='sts',
                                 region_name=aws_auth['region'],
                                 config=Config(retries={'max_attempts': 3})
                                 )

            assumed_role_object = sts.assume_role(
                RoleArn="{0}".format(aws_auth['role_name']),
                RoleSessionName='{0}'.format(aws_auth['role_session'])
            )
            # can be used ay name, but need to add restriction for the name!
            s3 = session.client(aws_access_key_id=assumed_role_object['Credentials']['AccessKeyId'],
                                aws_secret_access_key=assumed_role_object['Credentials']['SecretAccessKey'],
                                aws_session_token=assumed_role_object['Credentials']['SessionToken'],
                                service_name=aws_auth['client'],
                                region_name=aws_auth['region'],
                                config=Config(retries={'max_attempts': 3})
                                )

            return s3
        except Exception as err:
            print("Failed to create a boto3 client connection to S3:\n", str(err))
            logger.error('ERROR: Failed to create a boto3 client connection to S3')
            return False
    else:
        print('Please use/set [--profile-name] or [--role-name] with [--role-session]')
        return False


def s3_bucket(aws_auth, s3_bucket_name):
    s3_bucket_status = False
    s3 = s3_connector(aws_auth)
    if s3:
        try:
            s3.head_bucket(Bucket=s3_bucket_name)
            print("A bucket {} is already exists!".format(s3_bucket_name))
            s3_bucket_status = True
            return s3_bucket_status
        except botocore.exceptions.ClientError as e:
            error_code = int(e.response['Error']['Code'])
            if error_code == 403:
                print("Private {} bucket. Forbidden Access!".format(s3_bucket_name))
                logger.error('ERROR: Private {0} Bucket. Forbidden Access!'.format(s3_bucket_name))
            elif error_code == 404:
                print("The {} bucket does not exist!".format(s3_bucket_name))
                logger.error('ERROR: The {0} bucket does not exist!'.format(s3_bucket_name))
            s3_bucket_status = False
            return s3_bucket_status
    else:
        exit(-1)

    return s3_bucket_status


def s3_objects(aws_auth, s3_bucket_name):
    s3objects = []

    s3 = s3_connector(aws_auth)
    bucket_name = s3_bucket(aws_auth, s3_bucket_name)
    if bucket_name:
        try:
            for key in s3.list_objects(Bucket=s3_bucket_name)['Contents']:
                # print(key['Key'])
                key_name = key['Key']
                if (key_name.endswith('.gz')) or (key_name.endswith('.tar.gz')):
                    retr = s3.get_object(Bucket=s3_bucket_name, Key=key_name)
                    bytestream = BytesIO(retr['Body'].read())
                    content = GzipFile(None, 'rb', fileobj=bytestream).read().decode('utf-8')
                    s3objects.append(content)
                else:
                    data = s3.get_object(Bucket=s3_bucket_name, Key=key_name)
                    contents = data['Body'].read()
                    s3objects.append(contents)

                logger.info('SUCCESS: Retrieved object(s) from S3 {0} bucket'.format(s3_bucket_name))
        except Exception as e:
            print(e)
            logger.error('ERROR: I could not retrieved object(s) from S3 {0} bucket'.format(s3_bucket_name))

    return s3objects


def sending_data_to_elastisearch(es_url, docData):
    # Index each line to ES Domain
    index_name = globalVars['esIndexPrefix'] + str(datetime.date.today().year) + '-' + str(datetime.date.today().month)
    elastic_searh_url = es_url + '/' + index_name + '/' + globalVars['esIndexDocType']
    try:
        headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
        http = urllib3.PoolManager()
        response = http.request('POST',
                                elastic_searh_url,
                                body=json.dumps(docData),
                                headers=headers,
                                retries=False,
                                timeout=30)

        # print('Response status: ', response.status, "\nResponse data: ", response.data.decode('utf-8'))

        logger.error('ERROR [response status]: {0}'.format(response.status))

        if response.status == 201:
            logger.info('INFO: Response status: {0}\nResponse data: {1}'.format(response.status,
                                                                                response.data.decode('utf-8')))
            logger.info('INFO: Successfully inserted element into ES')
        elif response.status == 405:
            logger.error('ERROR: Something is wrong with sending DATA: \n\t {}'.format(response.data.decode('utf-8')))
            exit(1)
        else:
            logger.error('FAILURE: Got an error: \n\t {}'.format(response.data.decode('utf-8')))
            exit(1)
    except Exception as e:
        logger.error('ERROR: {0}'.format(str(e)))
        logger.error('ERROR: Unable to index line:"{0}"'.format(str(docData['content'])))
        print(e)
        exit(1)

    return sending_data_to_elastisearch


def pushing_locally(aws_auth, s3_bucket_name, es_url):
    s3objects = s3_objects(aws_auth, s3_bucket_name)
    for obj in s3objects:
        reader = csv.DictReader(StringIO(obj), fieldnames=None, restkey=None, restval=None, dialect='excel')
        for row in reader:
            json_out = json.loads(json.dumps(row))

            docData = {}
            docData['content'] = str(json.dumps(json_out))
            docData['createdDate'] = '{}'.format(datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"))

            sending_data_to_elastisearch(es_url, docData)

    return pushing_locally


def lambda_handler(event, context):

    aws_auth = {
        "client": os.environ['aws_boto3_client'],
        "region": os.environ['aws_region'],
        "profile_name": os.environ['aws_profile_name'],
        "role_name": os.environ['aws_role_name'],
        "role_session": os.environ['aws_role_session']
    }

    s3_bucket_name = os.environ['aws_s3_bucket_name']
    es_url = os.environ['elasticsearch_url']

    logger.info("Received event: " + json.dumps(event, indent=2))

    s3objects = s3_objects(aws_auth, s3_bucket_name)

    for obj in s3objects:
        reader = csv.DictReader(StringIO(obj.decode('utf-8')),
                                fieldnames=None,
                                restkey=None,
                                restval=None,
                                dialect='excel')
        for row in reader:
            json_out = json.loads(json.dumps(row))

            docData = {}
            docData['content'] = str(json.dumps(json_out))
            docData['createdDate'] = '{}'.format(datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"))

            sending_data_to_elastisearch(es_url, docData)
    logger.info('SUCCESS: Successfully indexed the entire doc into ElastiSearch')

    return {"Status": "AWS Lambda handler has been finished"}


if __name__ == '__main__':
    parser = argparse.ArgumentParser(prog='python3 script_name.py -h',
                                     usage='python3 script_name.py {ARGS}',
                                     add_help=True,
                                     prefix_chars='--/',
                                     epilog='''created by Vitalii Natarov'''
                                     )
    parser.add_argument('--version', action='version', version='v0.1.0')
    parser.add_argument('--bclient', dest='boto3_client', help='Set boto3 client', default='s3')
    parser.add_argument('--region', dest='region', help='Set AWS region for boto3', default='us-east-1')
    parser.add_argument('--pname', '--profile-name', dest='profile_name', help='Set profile name of AWS',
                        default=None)
    parser.add_argument('--rname', '--role-name', dest='role_name', help='Set role ARN name',
                        default=None)
    parser.add_argument('--rsession', '--role-session', dest='role_session', help='Set role session name',
                        default=None)
    parser.add_argument('--s3-bucket', '--s3bucket', dest='s3_bucket', help='Set S3 bucket name',
                        default="test-s3-to-elastisearch")
    parser.add_argument('--es-url', '--esurl', dest='es_url', help='Set ElastiSerch URL',
                        default="http://localhost:9200")
    parser.add_argument('--lambda', dest='aws_lambda', help='Set lambda usage', default=True)

    results = parser.parse_args()

    boto3_client = results.boto3_client
    region = results.region
    profile_name = results.profile_name
    role_name = results.role_name
    role_session = results.role_session
    s3_bucket_name = results.s3_bucket
    es_url = results.es_url
    aws_lambda = results.aws_lambda

    if aws_lambda == 'True':
        lambda_handler(None, None)
    else:
        start__time = time.time()
        aws_auth = {
            "client": boto3_client,
            "region": region,
            "profile_name": profile_name,
            "role_name": role_name,
            "role_session": role_session
        }

        pushing_locally(aws_auth, s3_bucket_name, es_url)

        end__time = round(time.time() - start__time, 2)
        print("--- %s seconds ---" % end__time)

Получить помощь, можно так:

$ python3 s3-to-elastisearch.py --help
usage: python3 script_name.py {ARGS}

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --bclient BOTO3_CLIENT
                        Set boto3 client
  --region REGION       Set AWS region for boto3
  --pname PROFILE_NAME, --profile-name PROFILE_NAME
                        Set profile name of AWS
  --rname ROLE_NAME, --role-name ROLE_NAME
                        Set role ARN name
  --rsession ROLE_SESSION, --role-session ROLE_SESSION
                        Set role session name
  --s3-bucket S3_BUCKET, --s3bucket S3_BUCKET
                        Set S3 bucket name
  --es-url ES_URL, --esurl ES_URL
                        Set ElastiSerch URL
  --lambda AWS_LAMBDA   Set lambda usage

created by Vitalii Natarov

Пример использования:

$ python3 s3-to-elastisearch.py --lambda=False --profile-name=default

Или:

$ python3 s3-to-elastisearch.py --lambda=False --role-name="role_here" --role-session="session"

Насчет AWS Lambda, то для нее нужно будет выставить Environment переменные со следующими ключами:

  • aws_s3_bucket_name — Опция которая позваляет задать имя для AWS S3 бакета (т.е где будут лежать ваши CSV файлы).
  • elasticsearch_url — Далавляем УРЛ ElasticSearch-а. Например — «localhost:9200».
  • boto3_client — Клиент для подключения, например — S3.
  • aws_region — Выставляем AWS регион, например: us-east-1.
  • aws_profile_name — Профиль для подключения и получения ресурсов с AWS. Например — default.
  • aws_role_name — Если не выставлен aws_profile_name, то нужно выставить aws_role_name для использования ресурсов в AWS.
  • role_session — Если не выставлен aws_profile_name, то нужно выставить role_session для использования ресурсов в AWS.

Например:

aws_lambda_role_trust_policies.json выглядат так:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "LambdaAssumeRoleAllow",
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

aws_lambda_policy.json выглядит так:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LogGroupAllows",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:167127734783:*"
        },
        {
            "Sid": "LogStreamAllows",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:167127734783:log-group:/aws/lambda/s3-to-elasticsearch:*"
            ]
        },
        {
            "Sid": "ESAllows",
            "Effect": "Allow",
            "Action": [
                "es:ListElasticsearchInstanceTypeDetails",
                "es:CreateElasticsearchDomain",
                "es:ListTags",
                "es:ESHttpDelete",
                "es:DeleteElasticsearchServiceRole",
                "es:GetUpgradeHistory",
                "es:ESHttpHead",
                "es:DeleteElasticsearchDomain",
                "es:DescribeElasticsearchDomain",
                "es:UpgradeElasticsearchDomain",
                "es:ESHttpPost",
                "es:ESHttpPatch",
                "es:DescribeElasticsearchDomains",
                "es:DescribeReservedElasticsearchInstanceOfferings",
                "es:CreateElasticsearchServiceRole",
                "es:ESHttpGet",
                "es:DescribeElasticsearchDomainConfig",
                "es:PurchaseReservedElasticsearchInstanceOffering",
                "es:DescribeReservedElasticsearchInstances",
                "es:ListDomainNames",
                "es:UpdateElasticsearchDomainConfig",
                "es:GetCompatibleElasticsearchVersions",
                "es:GetUpgradeStatus",
                "es:ListElasticsearchInstanceTypes",
                "es:ListElasticsearchVersions",
                "es:DescribeElasticsearchInstanceTypeLimits",
                "es:ESHttpPut"
            ],
            "Resource": "*"
        },
        {
            "Sid": "S3Allows",
            "Effect": "Allow",
            "Action": [
                "S3:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

python_script_role_policy.json:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LogGroupAllows",
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:167127734783:*"
        },
        {
            "Sid": "LogStreamAllows",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:167127734783:log-group:/aws/lambda/s3-to-elasticsearch:*"
            ]
        },
        {
            "Sid": "ESAllows",
            "Effect": "Allow",
            "Action": [
                "es:ListElasticsearchInstanceTypeDetails",
                "es:CreateElasticsearchDomain",
                "es:ListTags",
                "es:ESHttpDelete",
                "es:DeleteElasticsearchServiceRole",
                "es:GetUpgradeHistory",
                "es:ESHttpHead",
                "es:DeleteElasticsearchDomain",
                "es:DescribeElasticsearchDomain",
                "es:UpgradeElasticsearchDomain",
                "es:ESHttpPost",
                "es:ESHttpPatch",
                "es:DescribeElasticsearchDomains",
                "es:DescribeReservedElasticsearchInstanceOfferings",
                "es:CreateElasticsearchServiceRole",
                "es:ESHttpGet",
                "es:DescribeElasticsearchDomainConfig",
                "es:PurchaseReservedElasticsearchInstanceOffering",
                "es:DescribeReservedElasticsearchInstances",
                "es:ListDomainNames",
                "es:UpdateElasticsearchDomainConfig",
                "es:GetCompatibleElasticsearchVersions",
                "es:GetUpgradeStatus",
                "es:ListElasticsearchInstanceTypes",
                "es:ListElasticsearchVersions",
                "es:DescribeElasticsearchInstanceTypeLimits",
                "es:ESHttpPut"
            ],
            "Resource": "*"
        },
        {
            "Sid": "S3Allows",
            "Effect": "Allow",
            "Action": [
                "S3:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

python_script_role_trust_policies.json:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "sts.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Sid": "ff",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:sts::167127734783:assumed-role/test-lambda-role/s3-to-elasticsearch"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Можно использовать! Если нужно помочь в настройке, пишите, — смогу помочь!

И да, код будет отправлен в GitHub и его можно будет стянуть:

$ git clone git@github.com:SebastianUA/lambda-s3-elastisearch.git

Вот и все, статья «AWS Lambda на python3 в Unix/Linux» завершена.

The post AWS Lambda на python3 в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/aws-lambda-na-python3-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/ustanovka-lolcat-v-unix-linux/ https://linux-notes.org/ustanovka-lolcat-v-unix-linux/#respond Mon, 20 Jan 2020 21:49:49 +0000 https://linux-notes.org/?p=17648 Недавно, в интернете нашел утилиту под названием — lolcat. Сейчас расскажу что это за тулза и как ее установить на Unix/Linux ОС. Lolcat — это утилита которая выводит вывод команд которые раскрашиваются в радужный цвет. Установка lolcat в MacOS Для начала, нужно установить Homebrew утилиту и после этого, устанавливаем lolcat: Вот такая простая установка на […]

The post Установка lolcat в Unix/Linux first appeared on linux-notes.org.]]>

Недавно, в интернете нашел утилиту под названием — lolcat. Сейчас расскажу что это за тулза и как ее установить на Unix/Linux ОС.

Lolcat — это утилита которая выводит вывод команд которые раскрашиваются в радужный цвет.

Установка lolcat в MacOS

Для начала, нужно установить Homebrew утилиту и после этого, устанавливаем lolcat:

$ brew install lolcat git

Вот такая простая установка на МакОС. Использование данной утилиты будет внизу.

Установка lolcat в Unix/Linux

Для использования lolcat-а в Linux или Unix-подобных ОС, стоит установить для начала ruby + git.

В Debian-аноподобных ОС, можно выполнить:

# apt-get install -y ruby git

В RPM-подобных ОС:

# yum install -y ruby git

Или с использованием dnf:

# dnf install -y ruby git

Клонируем репу:

# git clone git@github.com:busyloop/lolcat.git

Или, можно обойтись wget/curl, например:

# wget https://github.com/busyloop/lolcat/archive/master.zip && unzip master.zip

Переходим в папку и запускаем билд:

# cd lolcat/bin && gem install lolcat

Или если скачивали через wget:

# cd lolcat-master/bin && gem install lolcat

Использование данной утилиты будет ниже.

Использование lolcat в Unix/Linux

Версию лолката можно проверить:

# lolcat --version
lolcat 100.0.0 (c)2011 moe@busyloop.net

Для помощи можно использовать:

$ lolcat -h

Вывод будет:

Данную утилиту можно использовать с различными командами, например:

$ ps | lolcat

Для вывода даты можно использовать:

$ date | lolcat

При использовании калькулятора:

$ cal | lolcat

На примере пинга:

$ ping google.com -c10 | lolcat

Просто вывести текст можно так:

$ echo "I like linux-notes.org" | lolcat -a -d 500

Для развлечений можно заюзать данную утилиту, но чтобы вывести с нее пользу — нет, не вижу профита.

Вот и все, статья «Установка lolcat в Unix/Linux» завершена.

The post Установка lolcat в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/ustanovka-lolcat-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/konvertaciya-vmware-virtual-machine-virtualbox-v-unix-linux/ https://linux-notes.org/konvertaciya-vmware-virtual-machine-virtualbox-v-unix-linux/#respond Mon, 20 Jan 2020 21:45:26 +0000 https://linux-notes.org/?p=17589 Если честно, VMware мне больше нравится чем VirtualBox, но лицензию я не хочу покупать для домашнего использования. По этому, выбор пал на то, чтобы перейти с VMware Fusion на VirtualBox. Для этого дела, мне пришлось зарегистрироваться на официальном сайте, заполнить данные. Но сразу я скачать не смог, возникли трудности с ПО и мне нужно было обратиться в поддержку. Поддержка решала данный вопрос около недели, но все решилось и я смог загрузить DMG на свой мак.

The post Конвертация VMware virtual machine -> VirtualBox в Unix/Linux first appeared on linux-notes.org.]]>

Если честно, VMware мне больше нравится чем VirtualBox, но лицензию я не хочу покупать для домашнего использования. По этому, выбор пал на то, чтобы перейти с VMware Fusion на VirtualBox. Для этого дела, мне пришлось зарегистрироваться на официальном сайте, заполнить данные. Но сразу я скачать не смог, возникли трудности с ПО и мне нужно было обратиться в поддержку. Поддержка решала данный вопрос около недели, но все решилось и я смог загрузить DMG на свой мак.

Конвертация VMware virtual machine -> VirtualBox на MacOS

Для начала, заходим на официальный сайт и качаем утилиту под названием «ovftool».

Установить DMG на МасОС через CLI можно так:

Установка dmg пакетов через CLI (командную строку) в MacOS X

Монтирование dmg образов через CLI (командную строку) в MacOS X

При использовании мака, утилита установится по следующему пути:

/Applications/VMware\ OVF\ Tool/ovftool

Чтобы получить помощь, выполните:

$ /Applications/VMware\ OVF\ Tool/ovftool -h
Usage: ovftool [options] <source> [<target>]
where
<source>: Source URL locator to an OVF package, VMX file, or virtual machine in
          vCenter or on ESX Server.
<target>: Target URL locator which specifies either a file location, or a
          location in the vCenter inventory or on an ESX Server.

If <target> is not specified, information about the source is displayed to the
console.

Options:
     --acceptAllEulas            : Accept all end-user licenses agreements
                                   without being prompted.
     --allowAllExtraConfig       : Whether we allow all the ExtraConfig
                                   options. These options are a security risk
                                   as they control low-level and potential
                                   unsafe options on the VM.
     --allowExtraConfig          : Whether we allow ExtraConfig options. These
                                   options are a security risk as they control
                                   low-level and potential unsafe options on
                                   the VM.
     --annotation                : Add annotation to vi, vmx, vapprun, vCloud,
                                   OVF, and OVA source locators
     --authdPortSource           : Use this to override default vmware authd
                                   port (902) when using a host as source.
     --authdPortTarget           : Use this to override default vmware authd
                                   port (902) when using a host as target.
     --chunkSize                 : Specifies the chunk size to use for files in
                                   a generated OVF package. The default is not
                                   to chunk. The chunk size without unit is
                                   assumed to be in megabytes. Accepted units
                                   are b, kb, mb, gb; e.g., 2gb or 100kb.
     --compress                  : Compress the disks in an OVF package. Value
                                   must be between 1 and 9. 1 is the fastest,
                                   but gives the worst compression, whereas 9
                                   is the slowest, but gives the best
                                   compression.
     --computerName              : Sets the computer name in the guest for a VM
                                   using the syntax --computerName:<VM
                                   ID>=<value>. Only applies to vCloud targets
                                   version 5.5 or newer.
     --coresPerSocket            : Specifies the distribution of the total
                                   number of CPUs over a number of virtual
                                   sockets using the syntax
                                   --coresPerSocket:<VM ID>=<value>. Only
                                   applies to vCloud targets version 5.5 or
                                   newer.
 -ds/--datastore                 : Target datastore name for a VI locator.
     --decodeBase64              : Decode option values with Base64.
     --defaultStorageProfile     : The storage profile for all VMs in the OVF
                                   package. The value should be an SPBM profile
                                   ID. Only applies to VI targets version 5.5
                                   or newer.
     --defaultStorageRawProfile  : The storage profile for all VMs in the OVF
                                   package. The value should be raw SPBM
                                   profile. The value will overwrite that in
                                   --defaultStorageProfile. Only applies to VI
                                   targets version 5.5 or newer.
     --deploymentOption          : Selects what deployment option to use (if
                                   the source OVF package supports multiple
                                   options.)
     --disableVerification       : Skip validation of signature and
                                   certificate.
 -dm/--diskMode                  : Select target disk format. Supported formats
                                   are: monolithicSparse, monolithicFlat,
                                   twoGbMaxExtentSparse, twoGbMaxExtentFlat,
                                   seSparse (VI target), eagerZeroedThick (VI
                                   target), thin (VI target), thick (VI
                                   target), sparse, and flat
     --diskSize                  : Sets the size of a VM disk in megabytes
                                   using the syntax --diskSize:<VM ID>,<disk
                                   instance ID>=<value>. Only applies to vCloud
                                   targets version 5.5 or newer.
     --eula                      : EULA to be inserted in the first virtual
                                   system or virtual system collection in the
                                   OVF. If the EULA is in a file, use the
                                   option --eula@=filename instead.
     --exportDeviceSubtypes      : Enables export of resource subtype for
                                   CD/Floppy/Parallel/Serial devices. This can
                                   limit portability as not all device backings
                                   are supported on all hypervisors. The
                                   default is false.
     --exportFlags               : Specifies one or more export flags to
                                   control what gets exported. The supported
                                   values for VI sources are mac, uuid, and
                                   extraconfig. Supported value for vCloud
                                   sources are preserveIdentity. One or more
                                   options can be provided, separated by
                                   commas.
     --extraConfig               : Sets an ExtraConfig element for all
                                   VirtualHardwareSections. The syntax is
                                   --extraConfig:<key>=<value>. Applies to vi,
                                   vmx, vapprun, vCloud, ovf, and ova source
                                   locators.
     --fencedMode                : If a parent network exists on the vCloud
                                   target, this property specifies the
                                   connectivity to the parent. Possible values
                                   are bridged, isolated, and natRouted.
 -h /--help                      : Prints this message.
     --hideEula                  : In OVF probe mode, hides the EULA.
     --ipAllocationPolicy        : IP allocation policy for a deployed OVF
                                   package.Supported values are: dhcpPolicy,
                                   transientPolicy, fixedPolicy,
                                   fixedAllocatedPolicy.
     --ipProtocol                : Select what IP protocol to use (IPv4, IPv6).
     --lax                       : Relax OVF specification conformance and
                                   virtual hardware compliance checks. Use only
                                   if you know what you are doing.
     --locale                    : Selects locale for target.
     --machineOutput             : Output OVF Tool messages in a machine
                                   friendly manner.
     --makeDeltaDisks            : Build delta disk hierarchy from the given
                                   source locator.
     --maxVirtualHardwareVersion : The maximal virtual hardware version to
                                   generate.
     --memorySize                : Sets the memory size in megabytes of a VM
                                   using the syntax --memorySize:<VM
                                   ID>=<value>. Only applies to vCloud targets
                                   version 5.5 or newer.
 -n /--name                      : Specifies target name (defaults to source
                                   name).
     --net                       : Set a network assignment in the deployed OVF
                                   package. A network assignment is set using
                                   the syntax --net:<OVF name>=<target name>.
                                   If the target is vCloud 5.5 or newer, a
                                   fence mode can also be specified using the
                                   syntax --net:<OVF name>=<target name>,<fence
                                   mode>. Possible fence mode values are:
                                   bridged, isolated, and natRouted.
 -nw/--network                   : Target network for a VI deployment.
     --nic                       : Specifies NIC configuration in a VM using
                                   the syntax --nic:<VM ID>,<index>=<OVF net
                                   name>,<isPrimary>,<ipAddressingMode>,<ipAddress>.
                                   Possible values for ipAddressingMode are:
                                   DHCP, POOL, MANUAL, and NONE. ipAddress is
                                   optional and should only be used when
                                   ipAddressingMode is set to MANUAL. Only
                                   applies to vCloud targets version 5.5 or
                                   newer.
     --noDisks                   : Disable disk conversion.
     --noImageFiles              : Do not include image files in destination.
     --noSSLVerify               : Skip SSL verification for VI connections.
     --numberOfCpus              : Sets the number of CPUs for a VM using the
                                   syntax --numberOfCpus:<VM ID>=<value>. Only
                                   applies to vCloud targets version 5.5 or
                                   newer.
 -o /--overwrite                 : Force overwrites of existing files.
     --powerOffSource            : Ensures a VM/vApp is powered off before
                                   importing from a VI source.
     --powerOffTarget            : Ensures a VM/vApp is powered off before
                                   overwriting a VI target.
     --powerOn                   : Powers on a VM/vApp deployed on a VI target.
     --privateKey                : Sign OVF package with the given private key
                                   (.pem file). The file must contain a private
                                   key and a certificate.
     --privateKeyPassword        : Password for the private key. Should be used
                                   in conjunction with privateKey if the
                                   private key requires password
                                   authentication. If required and not
                                   specified, the tool will prompt for the
                                   password.
     --prop                      : Set a property in the deployed OVF package.
                                   A property is set using the syntax
                                   --prop:<key>=<value>.
     --proxy                     : Proxy used for HTTP[S] access.
     --proxyNTLMAuth             : Enable NTLM authentication for proxy.
 -q /--quiet                     : No output to screen except errors.
     --schemaValidate            : Validate OVF descriptor against OVF schema.
     --shaAlgorithm              : Select SHA digest algorithm when creating
                                   OVF package. Supported values are SHA1,
                                   SHA256 and SHA512. Default value is SHA256.
     --skipManifestCheck         : Skip validation of OVF package manifest.
     --skipManifestGeneration    : Skip generation of OVF package manifest.
     --sourcePEM                 : File path to PEM formatted file used to
                                   verify VI connections.
     --sourceSSLThumbprint       : SSL fingerprint of SOURCE. OVF Tool verifies
                                   the SSL fingerprint it gets from SOURCE if
                                   the value is set.
 -st/--sourceType                : Explicitly express that source is OVF, OVA,
                                   VMX, VI, vCloud, ISO, FLP, vApprun
     --sslCipherList             : Use this to override default OpenSSL ciphers
                                   suite.
     --sslVersion                : Use this to set preferred TLS/SSL version
                                   for HTTPS connections. The valid values are
                                   as following:
                                     TLSv1_0: Set preferred TLS/SSL version to
                                   TLSv1.0.
                                     TLSv1_1: Set preferred TLS/SSL version to
                                   TLSv1.1.
                                     TLSv1_2: Set preferred TLS/SSL version to
                                   TLSv1.2.
     --storageProfile            : Sets the storage profile for a VM using the
                                   syntax --storageProfile:<VM ID>=<value>.
                                   Only applies to vCloud targets version 5.5
                                   or newer.
     --targetPEM                 : File path to PEM formatted file used to
                                   verify VI connections.
     --targetSSLThumbprint       : SSL fingerprint of TARGET. OVF Tool verifies
                                   the SSL fingerprint it gets from TARGET if
                                   the value is set.
 -tt/--targetType                : Explicitly express that target is OVF, OVA,
                                   VMX, VI, vCloud, ISO, FLP, vApprun
     --vCloudTemplate            : Create only a vApp template. Default value
                                   is false
     --vService                  : Set a vService assignment in the deployed
                                   OVF package. A vService assignment is set
                                   using the syntax
                                   --vService:<dependencyId>=<providerId>.
     --verifyOnly                : Do not upload the source but only verify it
                                   against the target host. Applies to VI 4
                                   targets only.
 -v /--version                   : Prints the version of this tool.
     --viCpuResource             : Specify the CPU resource settings for
                                   VI-locator targets. The syntax is
                                   --viCpuResource=<shares>:<reservation>:<limit>.
     --viMemoryResource          : Specify the CPU resource settings for
                                   VI-locator targets. The syntax is
                                   --viMemoryResource=<shares>:<reservation>:<limit>.
 -vf/--vmFolder                  : Target VM folder in VI inventory (relative
                                   to datacenter).

For more help, type: --help <topic>, where topics are:
 locators    : For detailed source and destination locator syntax
 examples    : For examples of use
 config      : For syntax of configuration files
 debug       : For debug purpose
 integration : For a list of options primarily used when ovftool is exec'ed
               from another tool or shellscript.

Использование довольно простое, например я использовал:

$ /Applications/VMware\ OVF\ Tool/ovftool /Users/captain/VirtualBox\ VMs/Windows\ 7/Windows\ 7\ x64.vmwarevm/Windows\ 7\ x64.vmx /Users/captain/VirtualBox\ VMs/Windows\ 7/Windows\ 7/windows_7.ovf
Opening VMX source: /Users/captain/VirtualBox VMs/Windows 7/Windows 7 x64.vmwarevm/Windows 7 x64.vmx
Opening OVF target: windows_7.ovf
Writing OVF package: windows_7.ovf
Disk progress: 5%

В папке которой был выполнен экспорт файлов, появятся файлы. Кликаем по файлу с «*.ovf» (у меня это windows_7.ovf):

Смотрим что хотим импортить, если что — изменяем и нажимаем на «Import». Импортинг занял 2 минуты у меня для Виндовс 7.

С приходом 64-битной ОС в МакОС, стало сложнее использование старые утилиты. Новые тоже не просто найти бесплатно. По этому, пришлось уйти с VMware…

Конвертация VMware virtual machine -> VirtualBox на Linux

В принципе — все тоже самое что я делал в МакОС, только нужно скачать бинарный файл для Linux-а. Если у кого-то возникнут трудность — смогу помочь! Пишите в комментариях возникшие ошибки и при первой возможности — отпишусь.

Вот и все, статья «Конвертация VMware virtual machine -> VirtualBox в Unix/Linux» завершена.

The post Конвертация VMware virtual machine -> VirtualBox в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/konvertaciya-vmware-virtual-machine-virtualbox-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/avtomatizaciya-aws-ebs-cherez-python-skript-v-unix-linux/ https://linux-notes.org/avtomatizaciya-aws-ebs-cherez-python-skript-v-unix-linux/#respond Sat, 11 Jan 2020 20:26:39 +0000 https://linux-notes.org/?p=16352 Буквально недавно появилось время написать статью и поделится моей автоматизацией. Задача заключалась в следующем, — нужно создать терраформ модуль(и) для провиженинга AWS EC2 с использованием AWS ASG + AWS EBS. Если кто-то работал с AWS ASG + AWS EBS volumes, то знают что терраформ не позволяет создавать и прикреплять волюмы (разделы) к автоскейленг-группе. На самом-то […]

The post Автоматизация AWS EBS через python скрипт и Terraform в Unix/Linux first appeared on linux-notes.org.]]>

Буквально недавно появилось время написать статью и поделится моей автоматизацией. Задача заключалась в следующем, — нужно создать терраформ модуль(и) для провиженинга AWS EC2 с использованием AWS ASG + AWS EBS. Если кто-то работал с AWS ASG + AWS EBS volumes, то знают что терраформ не позволяет создавать и прикреплять волюмы (разделы) к автоскейленг-группе. На самом-то деле, решений несколько:

  • Написать модуль для Terraform и подружить AWS ASG + AWS EBS volumes. Идея очень крутая и даже разумная, но, увы — я не знаю GO. На освоение потребуется довольно много времени. Сразу отпадает. Конечно, я очень хочу начать писать на данном языке именно для того, чтобы запилить недостающий функционал в Terraform. Ребята конечно, пишут хорошо модули, но, не так быстро как хотелось…..
  • Использовать python. Да, я питон знаю довольно хорошо и могу писать хорошие автоматизации. По этому, выбор пал именно на python3 + boto3.

В общим, нам понадобится, — Terraform:

Установка terraform в Unix/Linux

Модули для терраформа, можно взять мои с гитгаба:

$ git clone https://github.com/SebastianUA/terraform.git

Приведу пример моего терраформ примера и main файл выглядит:

module "sg" {
    source                              = "../../../modules/sg"
    name                                = "test"
    environment                         = "NonPROD"

    enable_sg_creating                  = true
    vpc_id                              = "vpc-09f1d36e"
    enable_all_egress_ports             = true
    allowed_ports                       = ["22", "7199", "7000", "7001", "9160", "9042"]
    allow_cidrs_for_allowed_ports       = {
        "22" = [
            "159.224.217.0/24",
            "10.0.0.0/8",
            "172.16.0.0/12"
        ],
        "7199" = [
            "10.0.0.0/8",
            "172.16.0.0/12"
        ],
        "7000" = [
            "10.0.0.0/8",
            "172.16.0.0/12"
        ],
        "7001" = [
            "10.0.0.0/8",
            "172.16.0.0/12"
        ],
        "9160" = [
            "10.0.0.0/8",
            "172.16.0.0/12"
        ],
        "9042" = [
            "10.0.0.0/8",
            "172.16.0.0/12"
        ]
    }

    enable_custom_sg_rule                  = true
    sg_rule_type                           = "ingress"
    sg_rule_from_port                      = "1"
    sg_rule_to_port                        = "65535"
    sg_rule_protocol                       = "all"
    #sg_rule_cidr_blocks                    = []
}

module "asg" {
    source             = "../../../modules/asg"
    name               = "test"
    region             = "us-west-2"
    environment        = "NonPROD"

    security_groups    = ["${module.sg.security_group_id}"]
    root_block_device  = [
        {
            volume_size = "8"
            volume_type = "gp2"
        },
    ]
    placement_tenancy        = "default"

    ami = {
        us-west-2 = "ami-09c6e771"
    }

    # Auto scaling group; NOTE: Use vpc_zone_identifier or availability_zones or will be got error!
    vpc_zone_identifier                 = ["subnet-ca0a9a83", "subnet-f2027b95"]
    # NOTE: If will be used availability_zones than enable_associate_public_ip_address = false!
    enable_associate_public_ip_address = false
    #
    enable_asg_azs                      = false

    health_check_type                   = "EC2"
    asg_min_size                        = 0
    asg_max_size                        = 1
    desired_capacity                    = 1
    wait_for_capacity_timeout           = 0

    monitoring                          = true
    # Set up the autoscaling schedule
    enable_autoscaling_schedule         = false
    asg_recurrence_scale_up             = "0 7 * * MON-FRI"
    asg_recurrence_scale_down           = "0 19 * * MON-FRI"

    # Define SSH key pair for our instances
    key_path                            = "additional_files/nonprod-cassandra.pub"

    # Set up launch configuration
    ec2_instance_type                   = "t3.medium"
    user_data                           = "./additional_files/bootstrap.tpl"
}

Файл (./additional_files/bootstrap.tpl) выглядит следующим образом:

#!/bin/bash

#----------------------------------------------------------------
# Updates; Install additional tools
#----------------------------------------------------------------
sudo yum update -y;
sudo yum install -y epel-release.noarch;
sudo yum update -y;
sudo yum install -y htop \
	telnet \
	wget \
	curl \
	python34 \
	python34-pip \
	net-tools \
	vim \
    git \
	screen
   
sudo yum update -y;
 
sudo echo "Test user_data file" >> ~/tmp.txt
#----------------------------------------------------------------
# Install AWS CLI tool
#----------------------------------------------------------------
sudo ln -s /usr/bin/pip-3.4 /usr/bin/pip3

sudo pip3 install awscli --upgrade --user
sudo pip3 install argparse boto3 awscli ec2-metadata --force-reinstall --upgrade 

#----------------------------------------------------------------
# Download and run py-script to attach Volume(s) to the node(s)
#----------------------------------------------------------------
git clone REPO_with_SCRIPT
python3 python-scripts/ebs_volumes.py --vol-name=test --pname=default --vol-size=6 --vol-env=nonprod --create
python3 python-scripts/ebs_volumes.py --vol-name=test --pname=default --attach

sudo rm -rf python-scripts

REPO_with_SCRIPT — Репозиторий где лежит скрипт который я приведу ниже:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import boto3
import time
import re

from ec2_metadata import ec2_metadata


class bgcolors:
    def __init__(self):
        self.colors = {
            'PURPURE': '\033[95m',
            'BLUE': '\033[94m',
            'GREEN': '\033[92m',
            'YELLOW': '\033[93m',
            'RED': '\033[91m',
            'ENDC': '\033[0m',
            'BOLD': '\033[1m',
            'UNDERLINE': '\033[4m'
        }


def ec2_connector(b3_client, region, prof_name):
    try:
        session = boto3.session.Session(profile_name=prof_name)
        ec2 = session.client(b3_client, region_name=region)
        return ec2
    except Exception as err:
        print("Failed to create a boto3 client connection to EC2:\n", bgcolors().colors['RED'] + str(err),
              bgcolors().colors['ENDC'])
        return False


def ec2_describe_parameters():
    instance_id = ec2_metadata.instance_id
    availability_zone = ec2_metadata.availability_zone
    parameters = {'Instance_ID': instance_id, 'Availability_Zone': availability_zone}
    return parameters


def ec2_describe_volumes(b3_client, region, prof_name):
    volumes_array = []
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2.describe_volumes(Filters=[])
        for volume in volumes['Volumes']:
            volume_id = volume['VolumeId']
            if 'Tags' in volume:
                tag_keys = volume['Tags']
                for tag_key in tag_keys:
                    tk = tag_key['Key']
                    if tk == "Name":
                        tv = tag_key['Value']
                        data_tags = {'Volume_Name': tv, 'Volume_ID': volume_id}
                        volumes_array.append(data_tags)
            else:
                data_tags = {'Volume_Name': 'None', 'Volume_ID': volume_id}
                volumes_array.append(data_tags)
    else:
        exit(-1)
    return volumes_array


def ec2_create_volume(b3_client, region, prof_name, vol_name, vol_env, vol_size):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        availability_zone = ec2_describe_parameters()['Availability_Zone']
        # availability_zone = 'us-west-2a'
        if not volumes:
            print('None, I will create a new volume!')
            try:
                new_volume = ec2.create_volume(Size=int(vol_size),
                                               Encrypted=False,
                                               AvailabilityZone='{0}'.format(availability_zone),
                                               VolumeType='gp2',
                                               TagSpecifications = [
                                                   {
                                                       "ResourceType": 'volume',
                                                       'Tags': [
                                                           {
                                                               'Key': 'Name',
                                                               'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
                                                           },
                                                           {
                                                               'Key': 'Environment',
                                                               'Value': '{0}'.format(vol_env)
                                                           },
                                                           {
                                                               'Key': 'Orchestration',
                                                               'Value': 'py-script'
                                                           },
                                                           {
                                                               'Key': 'Createdby',
                                                               'Value': 'Vitaliy Natarov'
                                                           }
                                                        ],
                                                    },
                                                ]
                )
                ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
            except Exception as err:
                print(
                    "I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                    bgcolors().colors['ENDC'])
        else:
            circle = 0
            founded_volumes = []
            while circle < len(volumes):
                if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
                    founded_volumes.append(volumes[circle])
                circle += 1
            if not founded_volumes:
                print('I will create a new volume!')
                try:
                    new_volume = ec2.create_volume(Size=int(vol_size),
                                                   Encrypted=False,
                                                   AvailabilityZone='{0}'.format(availability_zone),
                                                   VolumeType='gp2',
                                                   TagSpecifications=[
                                                       {
                                                           "ResourceType": 'volume',
                                                           'Tags': [
                                                               {
                                                                   'Key': 'Name',
                                                                   'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
                                                               },
                                                               {
                                                                   'Key': 'Environment',
                                                                   'Value': '{0}'.format(vol_env)
                                                               },
                                                               {
                                                                   'Key': 'Orchestration',
                                                                   'Value': 'py-script'
                                                               },
                                                               {
                                                                   'Key': 'Createdby',
                                                                   'Value': 'Vitaliy Natarov'
                                                               }
                                                           ],
                                                       },
                                                   ]
                                                   )
                    ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
                except Exception as err:
                    print(
                        "I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                        bgcolors().colors['ENDC'])
            else:
                print('Woops.... I cant create a new volume. Please use another name to it!!!!!')
    else:
        exit(-1)
    return ec2_create_volume


def ec2_attaching_volumes(b3_client, region, prof_name, vol_name):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        instance_id = ec2_describe_parameters()['Instance_ID']
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        #
        # instance_id = 'i-0f6f70b38d1ccf0c1'
        #
        # volumes = [{'Volume_Name': 'test-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1da'},
        #           {'Volume_Name': 'test2-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1db'}]
        if not volumes:
            print('None')
            exit(0)
        else:
            circle = 0
            founded_volumes = []
            while circle < len(volumes):
                if re.search(r'{0}'.format(vol_name), volumes[circle]['Volume_Name']):
                    founded_volumes.append(volumes[circle])
                circle += 1
            symbols_for_volumes = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']
            circle2 = 0
            while circle2 < len(founded_volumes):
                volume_device_name = '/dev/xvd{0}'.format(symbols_for_volumes[circle2])
                try:
                    print('I will try attach!')
                    print(volumes[circle2]['Volume_ID'], instance_id, volume_device_name)
                    ec2.attach_volume(
                        VolumeId='{0}'.format(volumes[circle2]['Volume_ID']),
                        InstanceId='{0}'.format(instance_id),
                        Device='{0}'.format(volume_device_name))
                except Exception as err:
                    print("I can not attach [{0}] volume to [{1}] node:\n".format(volumes[circle2]['Volume_ID'], instance_id),
                          bgcolors().colors['RED'] + str(err), bgcolors().colors['ENDC'])
                circle2 += 1
    else:
        exit(-1)
    return ec2_attaching_volumes


def ec2_delete_volume(b3_client, region, prof_name, vol_name, vol_env):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        circle = 0
        if volumes:
            while circle < len(volumes):
                if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
                    volume_id = volumes[circle]['Volume_ID']
                    try:
                        delete_volume = ec2.delete_volume(VolumeId='{0}'.format(volume_id))
                        # ec2.get_waiter('delete_complete').wait(VolumeIds=[delete_volume['VolumeId']])
                    except Exception as err:
                        print(
                            "I can not delete a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                        bgcolors().colors['ENDC'])
                circle += 1
        else:
            print('I dont have needed volume name to delete! Please use another one....')

    else:
        exit(-1)
    return ec2_delete_volume


def main():
    start__time = time.time()
    parser = argparse.ArgumentParser(prog='python3 script_name.py -h',
                                     usage='python3 script_name.py {ARGS}',
                                     add_help=True,
                                     prefix_chars='--/',
                                     epilog='''created by Vitalii Natarov''')
    parser.add_argument('--version', action='version', version='v0.5.0')
    parser.add_argument('--bclient', '--boto3-client', dest='boto3_client', help='Set boto3 client', default='ec2')
    parser.add_argument('--region', dest='region', help='Set AWS region for boto3', default='us-west-2')
    parser.add_argument('--pname', '--profile-name', dest='profile_name', help='Set profile name',
                        default='default')
    parser.add_argument('--vol-name', '--volume-name', dest='volume_name', help='Set volume name to find|attach it',
                        default='test')
    parser.add_argument('--vol-env', '--volume-env', dest='volume_env', help='Set env for volume', default='nonprod')
    parser.add_argument('--vol-size', '--volume-size', dest='volume_size', help='Set size for volume', default=6)

    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--d', dest='describe', help='Describe volumes', action='store_true')
    group.add_argument('--describe', dest='describe', help='Describe volumes', action='store_true')

    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument('--c', dest='create', help='Create volume', action='store_true', default=argparse.SUPPRESS)
    group2.add_argument('--create', dest='create', help='Create volume', action='store_true')

    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument('--a', dest='attach', help='Attach volume(s)', action='store_true', default=argparse.SUPPRESS)
    group3.add_argument('--attach', dest='attach', help='Attach volume(s)', action='store_true')

    group4 = parser.add_mutually_exclusive_group(required=False)
    group4.add_argument('--del', dest='delete', help='Delete volume', action='store_true', default=argparse.SUPPRESS)
    group4.add_argument('--delete', dest='delete', help='Delete volume', action='store_true')

    results = parser.parse_args()

    boto3_client = results.boto3_client
    region = results.region
    profile_name = results.profile_name
    volume_name = results.volume_name
    volume_env = results.volume_env
    volume_size = results.volume_size

    if results.describe:
        print(ec2_describe_volumes(boto3_client, region, profile_name))
    elif results.create:
        ec2_create_volume(boto3_client, region, profile_name, volume_name, volume_env, volume_size)
    elif results.attach:
        ec2_attaching_volumes(boto3_client, region, profile_name, volume_name)
    elif results.delete:
        ec2_delete_volume(boto3_client, region, profile_name, volume_name, volume_env)
    else:
        print(bgcolors().colors['GREEN'],
              'Please add [--describe] for describe or [--create] for create or [--attach] for attach',
              bgcolors().colors['ENDC'])
        print(bgcolors().colors['RED'], 'For help, use: script_name.py -h', bgcolors().colors['ENDC'])
        exit(0)

    end__time = round(time.time() - start__time, 2)
    print("--- %s seconds ---" % end__time)
    print(bgcolors().colors['GREEN'], "============================================================",
          bgcolors().colors['ENDC'])
    print(bgcolors().colors['GREEN'], "==========================FINISHED==========================",
          bgcolors().colors['ENDC'])
    print(bgcolors().colors['GREEN'], "============================================================",
          bgcolors().colors['ENDC'])


if __name__ == '__main__':
    main()

Вот такой вот скрипт. Чтобы вызвать помощь, выполните:

$ python3 ebs_volumes.py --help

Получаем вывод:

usage: python3 script_name.py {ARGS}

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit
  --bclient BOTO3_CLIENT, --boto3-client BOTO3_CLIENT
                        Set boto3 client
  --region REGION       Set AWS region for boto3
  --pname PROFILE_NAME, --profile-name PROFILE_NAME
                        Set profile name
  --vol-name VOLUME_NAME, --volume-name VOLUME_NAME
                        Set volume name to find|attach it
  --vol-env VOLUME_ENV, --volume-env VOLUME_ENV
                        Set env for volume
  --vol-size VOLUME_SIZE, --volume-size VOLUME_SIZE
                        Set size for volume
  --d                   Describe volumes
  --describe            Describe volumes
  --c                   Create volume
  --create              Create volume
  --a                   Attach volume(s)
  --attach              Attach volume(s)
  --del                 Delete volume
  --delete              Delete volume

created by Vitalii Natarov

Для тех кто не очень понимает, то скрипт умеет создавать, проверять какие томы имеются в AWS, прикреплять томы к нодам и удалять волюмы.

Приведу пару примеров использования:

$ python3 ebs_volumes.py --vol-name=test --pname=default --vol-size=66 --vol-env=nonprod --create
$ python3 ebs_volumes.py --vol-name=test --pname=default --describe
$ python3 ebs_volumes.py --vol-name=test --pname=default --delete

Как-то так. А у меня пока все, статья «Автоматизация AWS EBS через python скрипт и Terraform в Unix/Linux».

The post Автоматизация AWS EBS через python скрипт и Terraform в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/avtomatizaciya-aws-ebs-cherez-python-skript-v-unix-linux/feed/ 0
Page_c76a3fcf https://linux-notes.org/rabota-s-aws-ebs-v-unix-linux/ https://linux-notes.org/rabota-s-aws-ebs-v-unix-linux/#respond Sat, 11 Jan 2020 20:14:57 +0000 https://linux-notes.org/?p=17560 Решил сделать небольшую заметку о том, как работать с AWS EBS. Существует несколько способов. Расскажу как можно это сделать несколькими способами.

The post Работа с AWS EBS в Unix/Linux first appeared on linux-notes.org.]]>

Решил сделать небольшую заметку о том, как работать с AWS EBS. Существует несколько способов. Расскажу как можно это сделать несколькими способами.

Работа с AWS EBS в Unix/Linux (Ручная)

Покажу на наглядном примере как можно создать, примаунтить том к созданной машине. Начну с того, что создам раздел (AWS EBS волюму), для этого логинимся в AWS Console. Затем, переходим в EC2 раздел. Затем, Volume раздел. После этого, нажимаем на «Create Volume»:

После создание нового раздела, его стоит приатачить на нужный инстанс(машину), для этого, — открываем EC2 -> в блоке ELASTIC BLOCK STORE находим «Volumes», затем находим созданный раздел. Кликаем на нужную волюму правой клавишей мышки и выбираем «Attach Volume» (Так же можно нажать на «Actions -> Attach Volume»):

Зайду на сервер, у меня на нем крутиться Jenkins.

Создаем папку:

$ mkdir /var/build/garson_home

Смотрим какие диски есть в системе:

root@ip-10-101-44-43:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
loop0     7:0    0 12.6M  1 loop /snap/amazon-ssm-agent/295
loop1     7:1    0   87M  1 loop /snap/core/5145
xvda    202:0    0  100G  0 disk
`-xvda1 202:1    0  100G  0 part /
xvdf    202:80   0  1.5T  0 disk
root@ip-10-101-44-43:~#

ВНИМАНИЕ! Определите, есть ли файловая система на томе(волюме). Новые тома являются необработанными блочными устройствами, и вы должны создать на них файловую систему, прежде чем сможете их монтировать и использовать. Тома, которые были восстановлены из снапшетов, вероятно, уже имеют файловую систему; если вы создаете новую файловую систему поверх существующей файловой системы, операция перезаписывает ваши данные.

Используйте команду «file -s», чтобы получить информацию об устройстве, например, тип файловой системы:

root@ip-10-101-44-43:~# file -s /dev/xvdf
/dev/xvdf: Linux rev 1.0 ext4 filesystem data, UUID=aee3cf72-5a23-4ea6-9c50-af100dfc0912 (needs journal recovery) (extents) (large files) (huge files)
root@ip-10-101-44-43:~#

Сейчас можем смонитировать:

root@ip-10-101-44-43:~# mount /dev/xvdf /var/build/garson_home
root@ip-10-101-44-43:~#

Если получили ошибку:

mount: /var/build/garson_home: wrong fs type, bad option, bad superblock on /dev/xvdf, missing codepage or helper program, or other error.

То решение — форматирование волюмы:

# mkfs -t ext4 /dev/xvdf
mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 157286400 4k blocks and 39321600 inodes
Filesystem UUID: e91fac36-3163-415c-a583-b390cce264c7
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
	4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
	102400000

Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done

и потом можно сново монтировать:

# mount /dev/xvdf /var/build/garson_home

Чтобы отмонтировать раздел, стоит выполнить:

# umount /var/build/garson_home

Получил ошибку при удалении папки:

rm: cannot remove '/var/build/garson_home': Device or resource busy

Фикс:

root@ip-10-101-40-26:~# losetup -d /dev/loop0
root@ip-10-101-40-26:~# fuser -vm /var/build/garson_home
                     USER        PID ACCESS COMMAND
/var/build/garson_home:
                     root     kernel mount /var/build/garson_home
root@ip-10-101-40-26:~# losetup -d /dev/loop1
root@ip-10-101-40-26:~# fuser -vm /var/build/garson_home
                     USER        PID ACCESS COMMAND
/var/build/garson_home:
                     root     kernel mount /var/build/garson_home
root@ip-10-101-40-26:~# losetup -a
/dev/loop1: [66309]:62992 (/var/lib/snapd/snaps/core_5145.snap)
/dev/loop0: [66309]:62995 (/var/lib/snapd/snaps/amazon-ssm-agent_295.snap)
root@ip-10-101-40-26:~# umount /var/build/garson_home
root@ip-10-101-40-26:~#

Как-то так. Все вроде бы логично и работает, но…. при следующем перезапуске ОС, раздел не будет автоматически атачиться к файловой системе, для этого — нужно добавить некоторые действия в fstab.

Для начала, скопируем конфиг:

# cp /etc/fstab /etc/fstab.orig

Это не обязательно, но если что-то пойдет не так — можно будет откатиться назад.

Используйте команду blkid, чтобы найти UUID устройства:

# blkid

Открываем fstab:

# vim /etc/fstab

И прописываем:

UUID=YOUR_UUID_HERE  /YOUR_MOUNT_PATH_HERE  TYPE_FS_HERE  defaults,nofail  DUMP_HERE  PASS_HERE

Где:

  • YOUR_UUID_HERE — Это UUID, который получили с вывода команды blkid для вашего раздела (Пример: bc756bb8-a3cb-4c07-90b9-8827e73687c9).
  • YOUR_MOUNT_PATH_HERE — Это путь для монтирования (Например: /var/build/garson_home).
  • TYPE_FS_HERE — Это тип монтируемой ФС (Например: xfs).
  • defaults,nofail — Это опции монтирования.
  • DUMP_HERE — Используется утилитой dump для того чтобы определить, когда делать резервную копию. После установки, dump проверяет эту запись и использует значение, чтобы решить, подключать ли файловую систему. Возможные значения 0 или 1. Если 0, dump игнорирует файловую систему, если 1, dump сделает резервную копию. У большинства пользователей dump не установлен, поэтому в поле следует задать 0. Например: 0.
  • PASS_HERE — Это номер прохода. Утилита fsck проверяет число, подставленное в поле и решает, в каком порядке проверять файловую систему. Возможные значения 0, 1 и 2. Файловые системы со значением , равным 0, не будут проверены утилитой fsck. У корневой системы должен быть наибольший приоритет, 1, остальные файловые системы должны иметь приоритет 2. Например: 2.

В интернете видел как некоторые юзают:

YOUR_SECTION_HERE   /YOUR_MOUNT_PATH_HERE   TYPE_FS_HERE    defaults,nofail   DUMP_HERE   PASS_HERE

Где:

  • YOUR_SECTION_HERE — это название раздела. Например: /dev/xvdf. Получить можно с помощью утилиты lsblk.

Ну что, отмонтируем волюму:

# umount /YOUR_MOUNT_PATH_HERE

И примонтируем все:

# mount -a

Работа с AWS EBS в Unix/Linux (Terraform)

Так же, можно написать Терраформ модуль для работы с AWS EBS. У меня он уже имеется и его можно стянуть:

$ git clone git@github.com:SebastianUA/terraform.git

В данном репозитории лежат все мои модули что я писал. Конечно же, там есть и для работы с AWS EBS.

Работа с AWS EBS в Unix/Linux (Python + boto3)

Написал скрипт (Название скрипта будет ebs_volumes.py) и его содержание:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import boto3
import time
import re

from ec2_metadata import ec2_metadata


class bgcolors:
    def __init__(self):
        self.colors = {
            'PURPURE': '\033[95m',
            'BLUE': '\033[94m',
            'GREEN': '\033[92m',
            'YELLOW': '\033[93m',
            'RED': '\033[91m',
            'ENDC': '\033[0m',
            'BOLD': '\033[1m',
            'UNDERLINE': '\033[4m'
        }


def ec2_connector(b3_client, region, prof_name):
    try:
        session = boto3.session.Session(profile_name=prof_name)
        ec2 = session.client(b3_client, region_name=region)
        return ec2
    except Exception as err:
        print("Failed to create a boto3 client connection to EC2:\n", bgcolors().colors['RED'] + str(err),
              bgcolors().colors['ENDC'])
        return False


def ec2_describe_parameters():
    instance_id = ec2_metadata.instance_id
    availability_zone = ec2_metadata.availability_zone
    parameters = {'Instance_ID': instance_id, 'Availability_Zone': availability_zone}
    return parameters


def ec2_describe_volumes(b3_client, region, prof_name):
    volumes_array = []
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2.describe_volumes(Filters=[])
        for volume in volumes['Volumes']:
            volume_id = volume['VolumeId']
            if 'Tags' in volume:
                tag_keys = volume['Tags']
                for tag_key in tag_keys:
                    tk = tag_key['Key']
                    if tk == "Name":
                        tv = tag_key['Value']
                        data_tags = {'Volume_Name': tv, 'Volume_ID': volume_id}
                        volumes_array.append(data_tags)
            else:
                data_tags = {'Volume_Name': 'None', 'Volume_ID': volume_id}
                volumes_array.append(data_tags)
    else:
        exit(-1)
    return volumes_array


def ec2_create_volume(b3_client, region, prof_name, vol_name, vol_env, vol_size):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        availability_zone = ec2_describe_parameters()['Availability_Zone']
        # availability_zone = 'us-west-2a'
        if not volumes:
            print('None, I will create a new volume!')
            try:
                new_volume = ec2.create_volume(Size=int(vol_size),
                                               Encrypted=False,
                                               AvailabilityZone='{0}'.format(availability_zone),
                                               VolumeType='gp2',
                                               TagSpecifications = [
                                                   {
                                                       "ResourceType": 'volume',
                                                       'Tags': [
                                                           {
                                                               'Key': 'Name',
                                                               'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
                                                           },
                                                           {
                                                               'Key': 'Environment',
                                                               'Value': '{0}'.format(vol_env)
                                                           },
                                                           {
                                                               'Key': 'Orchestration',
                                                               'Value': 'py-script'
                                                           },
                                                           {
                                                               'Key': 'Createdby',
                                                               'Value': 'Vitaliy Natarov'
                                                           }
                                                        ],
                                                    },
                                                ]
                )
                ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
            except Exception as err:
                print(
                    "I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                    bgcolors().colors['ENDC'])
        else:
            circle = 0
            founded_volumes = []
            while circle < len(volumes):
                if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
                    founded_volumes.append(volumes[circle])
                circle += 1
            if not founded_volumes:
                print('I will create a new volume!')
                try:
                    new_volume = ec2.create_volume(Size=int(vol_size),
                                                   Encrypted=False,
                                                   AvailabilityZone='{0}'.format(availability_zone),
                                                   VolumeType='gp2',
                                                   TagSpecifications=[
                                                       {
                                                           "ResourceType": 'volume',
                                                           'Tags': [
                                                               {
                                                                   'Key': 'Name',
                                                                   'Value': '{0}-ebs-{1}'.format(vol_name, vol_env)
                                                               },
                                                               {
                                                                   'Key': 'Environment',
                                                                   'Value': '{0}'.format(vol_env)
                                                               },
                                                               {
                                                                   'Key': 'Orchestration',
                                                                   'Value': 'py-script'
                                                               },
                                                               {
                                                                   'Key': 'Createdby',
                                                                   'Value': 'Vitaliy Natarov'
                                                               }
                                                           ],
                                                       },
                                                   ]
                                                   )
                    ec2.get_waiter('volume_available').wait(VolumeIds=[new_volume['VolumeId']])
                except Exception as err:
                    print(
                        "I can not create a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                        bgcolors().colors['ENDC'])
            else:
                print('Woops.... I cant create a new volume. Please use another name to it!!!!!')
    else:
        exit(-1)
    return ec2_create_volume


def ec2_attaching_volumes(b3_client, region, prof_name, vol_name):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        instance_id = ec2_describe_parameters()['Instance_ID']
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        #
        # instance_id = 'i-0f6f70b38d1ccf0c1'
        #
        # volumes = [{'Volume_Name': 'test-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1da'},
        #           {'Volume_Name': 'test2-ebs-nonprod', 'Volume_ID': 'vol-040d07848d558e1db'}]
        if not volumes:
            print('None')
            exit(0)
        else:
            circle = 0
            founded_volumes = []
            while circle < len(volumes):
                if re.search(r'{0}'.format(vol_name), volumes[circle]['Volume_Name']):
                    founded_volumes.append(volumes[circle])
                circle += 1
            symbols_for_volumes = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n']
            circle2 = 0
            while circle2 < len(founded_volumes):
                volume_device_name = '/dev/xvd{0}'.format(symbols_for_volumes[circle2])
                try:
                    print('I will try attach!')
                    print(volumes[circle2]['Volume_ID'], instance_id, volume_device_name)
                    ec2.attach_volume(
                        VolumeId='{0}'.format(volumes[circle2]['Volume_ID']),
                        InstanceId='{0}'.format(instance_id),
                        Device='{0}'.format(volume_device_name))
                except Exception as err:
                    print("I can not attach [{0}] volume to [{1}] node:\n".format(volumes[circle2]['Volume_ID'], instance_id),
                          bgcolors().colors['RED'] + str(err), bgcolors().colors['ENDC'])
                circle2 += 1
    else:
        exit(-1)
    return ec2_attaching_volumes


def ec2_delete_volume(b3_client, region, prof_name, vol_name, vol_env):
    ec2 = ec2_connector(b3_client, region, prof_name)
    if ec2:
        volumes = ec2_describe_volumes(b3_client, region, prof_name)
        circle = 0
        if volumes:
            while circle < len(volumes):
                if re.search(r'{0}-ebs-{1}'.format(vol_name, vol_env), volumes[circle]['Volume_Name']):
                    volume_id = volumes[circle]['Volume_ID']
                    try:
                        delete_volume = ec2.delete_volume(VolumeId='{0}'.format(volume_id))
                        # ec2.get_waiter('delete_complete').wait(VolumeIds=[delete_volume['VolumeId']])
                    except Exception as err:
                        print(
                            "I can not delete a [{0}] volume:\n".format(vol_name), bgcolors().colors['RED'] + str(err),
                        bgcolors().colors['ENDC'])
                circle += 1
        else:
            print('I dont have needed volume name to delete! Please use another one....')

    else:
        exit(-1)
    return ec2_delete_volume


def main():
    start__time = time.time()
    parser = argparse.ArgumentParser(prog='python3 script_name.py -h',
                                     usage='python3 script_name.py {ARGS}',
                                     add_help=True,
                                     prefix_chars='--/',
                                     epilog='''created by Vitalii Natarov''')
    parser.add_argument('--version', action='version', version='v0.5.0')
    parser.add_argument('--bclient', '--boto3-client', dest='boto3_client', help='Set boto3 client', default='ec2')
    parser.add_argument('--region', dest='region', help='Set AWS region for boto3', default='us-west-2')
    parser.add_argument('--pname', '--profile-name', dest='profile_name', help='Set profile name',
                        default='default')
    parser.add_argument('--vol-name', '--volume-name', dest='volume_name', help='Set volume name to find|attach it',
                        default='test')
    parser.add_argument('--vol-env', '--volume-env', dest='volume_env', help='Set env for volume', default='nonprod')
    parser.add_argument('--vol-size', '--volume-size', dest='volume_size', help='Set size for volume', default=6)

    group = parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--d', dest='describe', help='Describe volumes', action='store_true')
    group.add_argument('--describe', dest='describe', help='Describe volumes', action='store_true')

    group2 = parser.add_mutually_exclusive_group(required=False)
    group2.add_argument('--c', dest='create', help='Create volume', action='store_true', default=argparse.SUPPRESS)
    group2.add_argument('--create', dest='create', help='Create volume', action='store_true')

    group3 = parser.add_mutually_exclusive_group(required=False)
    group3.add_argument('--a', dest='attach', help='Attach volume(s)', action='store_true', default=argparse.SUPPRESS)
    group3.add_argument('--attach', dest='attach', help='Attach volume(s)', action='store_true')

    group4 = parser.add_mutually_exclusive_group(required=False)
    group4.add_argument('--del', dest='delete', help='Delete volume', action='store_true', default=argparse.SUPPRESS)
    group4.add_argument('--delete', dest='delete', help='Delete volume', action='store_true')

    results = parser.parse_args()

    boto3_client = results.boto3_client
    region = results.region
    profile_name = results.profile_name
    volume_name = results.volume_name
    volume_env = results.volume_env
    volume_size = results.volume_size

    if results.describe:
        print(ec2_describe_volumes(boto3_client, region, profile_name))
    elif results.create:
        ec2_create_volume(boto3_client, region, profile_name, volume_name, volume_env, volume_size)
    elif results.attach:
        ec2_attaching_volumes(boto3_client, region, profile_name, volume_name)
    elif results.delete:
        ec2_delete_volume(boto3_client, region, profile_name, volume_name, volume_env)
    else:
        print(bgcolors().colors['GREEN'],
              'Please add [--describe] for describe or [--create] for create or [--attach] for attach',
              bgcolors().colors['ENDC'])
        print(bgcolors().colors['RED'], 'For help, use: script_name.py -h', bgcolors().colors['ENDC'])
        exit(0)

    end__time = round(time.time() - start__time, 2)
    print("--- %s seconds ---" % end__time)
    print(bgcolors().colors['GREEN'], "============================================================",
          bgcolors().colors['ENDC'])
    print(bgcolors().colors['GREEN'], "==========================FINISHED==========================",
          bgcolors().colors['ENDC'])
    print(bgcolors().colors['GREEN'], "============================================================",
          bgcolors().colors['ENDC'])


if __name__ == '__main__':
    main()

Вот такой вот скрипт. Чтобы вызвать помощь, выполните:

$ python3 ebs_volumes.py --help

Вот и все, статья «Работа с AWS EBS в Unix/Linux» завершена.

The post Работа с AWS EBS в Unix/Linux first appeared on linux-notes.org.]]>
https://linux-notes.org/rabota-s-aws-ebs-v-unix-linux/feed/ 0