Управление объектами AWS через Python

Опубликовано: 19.03.2021
Автор: Виталий Бочкарев
Поддержать автора статьи по этой ссылке

Для взаимодействия с облачной инфраструктурой AWS компания Amazon поддерживает открытый проект на GitHub, который реализует Python SDK. С помощью библиотек из этого проекта можно реализовать взаимодействие с облачной инфраструктурой посредством скриптов или даже написать собственный облегченный веб-сервис для AWS. Полная документация к библиотекам опубликована в библиотеке Amazon.

 

print('--- importing module_aws.py ---')
# импортирование модулей, установленных в системе
from boto3 import client as aws_client
from botocore.config import Config
from datetime import datetime, timedelta
from os import path as os_path
from os import getcwd as os_getcwd
from os import environ as os_environ
from sys import path as sys_path
# импортирование модулей из проекта
try:
  print('  trying to import modules with relative paths')
  from .module_core import instances_json_read
  from django.conf import settings
  print('  relative import is OK')
except ImportError:
  print('  relative import failed')
  print('  trying to import modules with absolute paths')
  try:
    # получение текущей дирректории, откуда запущен скрипт - должен быть корень проекта
    current_directory = os_getcwd()
    # добавление пути к папке проекта в переменные среды
    sys_path.append(current_directory)
    # подключение настроек проекта
    os_environ.setdefault('DJANGO_SETTINGS_MODULE', 'webconsole.settings')
    # импортирование переменных Django в текущую среду
    from django.conf import settings
    from module_core import instances_json_read
    print('  absolute import is OK')
  except ModuleNotFoundError:
    print('  absolute import failed')
    exit()
print()


 
aws_access_key_id = settings.AWS_ACCESS_KEY
aws_secret_access_key = settings.AWS_SECRET_KEY
region_name = settings.AWS_REGION
proxies = settings.PROXY_SETTINGS['proxies']
aws_ec2_types = settings.AWS_EC2_TYPES
 
def aws_ec2_client(aws_access_key_id, aws_secret_access_key, region_name, proxies):
  client = aws_client(
      'ec2',
      aws_access_key_id=aws_access_key_id,
      aws_secret_access_key=aws_secret_access_key,
      region_name=region_name,
      config=Config(proxies=proxies),
    )
  return client


 
def aws_cloudwatch_client(aws_access_key_id, aws_secret_access_key, region_name, proxies):
  client = aws_client(
      'cloudwatch',
      aws_access_key_id=aws_access_key_id,
      aws_secret_access_key=aws_secret_access_key,
      region_name=region_name,
      config=Config(proxies=proxies),
    )
  return client


 
def aws_instance_properties_local(id):
  #‭ ‬путь к json-файлу
  instances_filepath = os_path.join(settings.BASE_DIR, settings.PROJECT_INSTANCES_JSON_FILEPATH)
  # запрос данных из json-файла
  try:
    instances_table = instances_json_read(instances_filepath)['aws_vm_table']
    instance_line = instances_table.loc[id.lower()].to_dict()
  except:
    instance_line = ''
  return instance_line


 
def aws_instance_properties_mixed(id):
  # получение локальных данных из файла
  vm_properties = aws_instance_properties_local(id)
  # получение облачных данных
  ec2_client = aws_ec2_client(aws_access_key_id, aws_secret_access_key, region_name, proxies)
  response = ec2_client.describe_instances(Filters=[{
    'Name': 'instance-id',
    'Values': [id,]
  },])
  instance_attr = response['Reservations'][0]['Instances'][0]
  tags = {}
  for item in instance_attr['Tags']:
    tags.update({item['Key']: item['Value']})
  i_id = instance_attr['InstanceId']
  # описание сервера
  i_description = ''
  if 'Description' in tags:
    i_description = tags['Description']
  # назначение сервера
  i_purpose = ''
  if 'Description' in tags:
    i_purpose = tags['Purpose']
  # расписание резервного копирования сервера
  i_backup = ''
  if 'cpm backup' in tags:
    i_backup = tags['cpm backup']
  # расписание работы сервера
  i_schedule = ''
  if 'StartStopSchedule' in tags:
    i_schedule = tags['StartStopSchedule']
  # список доступа к управлению питанием
  i_start_stop_access = ''
  if 'StartStopAccess' in tags:
    i_start_stop_access = tags['StartStopAccess'].lower()
  # состояние сервера
  i_state = instance_attr['State']['Name']
  # построение словаря атрибутов
  curValues = {
    'id': i_id,
    'name': vm_properties['name'],
    'type': vm_properties['type'],
    'zone': vm_properties['vpc_description'],
    'ip_address': vm_properties['ip_address'],
    'responsible': vm_properties['responsible'],
    'project': vm_properties['project'],
    'description': i_description,
    'purpose': i_purpose,
    'state': i_state,
    'backup': i_backup,
    'schedule': i_schedule,
    'schedule_description': '',
    'start_stop_access': i_start_stop_access,
  }
  return curValues


 
def aws_instance_start(vm_id):
  ec2_client = aws_ec2_client(aws_access_key_id, aws_secret_access_key, region_name, proxies)
  result = ec2_client.start_instances(InstanceIds=[vm_id,])
  return result


 
def aws_instance_stop(vm_id):
  ec2_client = aws_ec2_client(aws_access_key_id, aws_secret_access_key, region_name, proxies)
  result = ec2_client.stop_instances(InstanceIds=[vm_id,])
  return result


 
def aws_instance_save(vm_id, description, purpose, backup_name, schedule_name, start_stop_access):
  # построение коллекции тэгов
  update_tags = []
  delete_tags = []
  # заполнение тэгов
  update_tags.append({'Key': 'Description', 'Value': description},)
  update_tags.append({'Key': 'Purpose', 'Value': purpose},)
  if not (backup_name == ''):
    update_tags.append({'Key': 'cpm backup', 'Value': backup_name},)
  else:
    delete_tags.append({'Key': 'cpm backup',})
  if not (schedule_name == ''):
    update_tags.append({'Key': 'StartStopSchedule', 'Value': schedule_name},)
  else:
    delete_tags.append({'Key': 'StartStopSchedule',})
  if not (start_stop_access == ''):
    # заменить "," и пробелы на ";", преобразовать в строчные
    start_stop_access = start_stop_access.replace(' ',';').replace(',',';').lower()
    # преобразовать строку в список
    start_stop_access_list = start_stop_access.split(";")
    # удалить пустые элементы
    start_stop_access_list = list(filter(None, start_stop_access_list))
    # удалить дубликаты из списка (преобразовать список в множество и обратно в список)
    start_stop_access_list = list(set(start_stop_access_list))
    # сортировка списка
    start_stop_access_list.sort()
    # преобразовать список в строку
    start_stop_access = ';'.join(start_stop_access_list)
    update_tags.append({'Key': 'StartStopAccess', 'Value': start_stop_access},)
  else:
    delete_tags.append({'Key': 'StartStopAccess',})
  # создание клиента подключения к AWS EC2
  ec2_client = aws_ec2_client(aws_access_key_id, aws_secret_access_key, region_name, proxies)
  # выполнение запроса на удаление тэгов в AWS EC2
  if len(delete_tags) > 0 :
    result = ec2_client.delete_tags(
      Resources=[vm_id,],
      Tags=delete_tags,
    )
  # выполнение запроса на обновление тэгов в AWS EC2
  if len(update_tags) > 0 :
    result = ec2_client.create_tags(
      Resources=[vm_id,],
      Tags=update_tags,
    )
  return


 
def aws_instance_metrics(vm_id):
  import pandas as pd
  cw_client = aws_cloudwatch_client(aws_access_key_id, aws_secret_access_key, region_name, proxies)
  time_start = datetime.utcnow() - timedelta(hours=8)
  time_stop = datetime.utcnow()
  vm_properties = aws_instance_properties_local(vm_id)
  response = cw_client.get_metric_statistics(
    Namespace='AWS/EC2',
    MetricName='CPUUtilization',
    StartTime=time_start,
    EndTime=time_stop,
    Statistics=['Maximum'],
    Period=300,
    Dimensions=[
        {
            'Name': 'InstanceId',
            'Value': vm_id
        },
    ],
  )
  json_data = response['Datapoints']
  if len(json_data) > 0:
    res_table = pd.json_normalize(json_data)
    res_table.sort_values(['Timestamp'], ascending=[True], inplace=True)
    res_table['Time'] = res_table['Timestamp'].apply(lambda x: (x+timedelta(hours=1)).strftime('%H:%M'))
    res_table.set_index('Timestamp', inplace=True)
    # переименование колонок в таблице
    res_table.rename(inplace=True, columns={'Maximum': 'CPU'})
    if ('ws' in vm_properties['name']):
      response = cw_client.get_metric_statistics(
        Namespace='AGC/Windows',
        MetricName='Memory Physical Available',
        StartTime=time_start,
        EndTime=time_stop,
        Statistics=['Maximum'],
        Period=300,
        Dimensions=[
            {
                'Name': 'InstanceId',
                'Value': vm_id
            },
            {
                'Name': 'instance',
                'Value': '_Total',
            },
            {
                'Name': 'objectname',
                'Value': 'Memory',
            },
        ],
      )
      json_data = response['Datapoints']
      if len(json_data) > 0:
        ram_table = pd.json_normalize(json_data)
        ram_table.rename(inplace=True, columns={'Maximum': 'RAM'})
        ram_table.set_index('Timestamp', inplace=True)
        res_table = res_table.join(ram_table,rsuffix='_r')
        # конвертирование доступной памяти из байт в мегабайт
        res_table['RAM'] = res_table['RAM'] / 1048576
        if (vm_properties['type'] in aws_ec2_types):
          # получение размера памяти и конвертирование из гигабайтов в магабайты
          vm_ram = aws_ec2_types[vm_properties['type']]['ram'] * 1024
        else:
          vm_ram = 0
        # вычислеине используемой памяти в процентах
        res_table['RAM'] = round((vm_ram - res_table['RAM']) / vm_ram * 100)
      else:
        res_table['RAM'] = 0
    else:
      response = cw_client.get_metric_statistics(
        Namespace='AGC/Linux',
        MetricName='Memory Utilization Percent',
        StartTime=time_start,
        EndTime=time_stop,
        Statistics=['Maximum'],
        Period=300,
        Dimensions=[
            {
                'Name': 'InstanceId',
                'Value': vm_id
            },
        ],
      )
      json_data = response['Datapoints']
      if len(json_data) > 0:
        ram_table = pd.json_normalize(json_data)
        ram_table.rename(inplace=True, columns={'Maximum': 'RAM'})
        ram_table.set_index('Timestamp', inplace=True)
        res_table = res_table.join(ram_table,rsuffix='_r')
      else:
        res_table['RAM'] = 0
    res_table = res_table.fillna(0)
    result = res_table[['Time', 'CPU', 'RAM']].values.tolist()
  else:
    result = ''
  return result


 
if __name__ == '__main__':
  print(datetime.today())
  result = aws_instance_metrics('i-1234567890abcdef')
  print(result)