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

import argparse
import logging
import os
import re
import sys
import yaml
from copy import deepcopy

import saas.tools.ssm.modules.dm_api as dm_api
import saas.tools.ssm.modules.nanny_api as nanny_api
import saas.tools.ssm.modules.startrek_api as startrek_api
from saas.tools.ssm.modules.misc import load_and_write_resources, get_ferryman_name
from saas.tools.ssm.modules.nanny_yp_api import SaaSNannyYpWorkflow

from saas.tools.devops.lib2.saas_dj_service import SaasDJService
from saas.tools.devops.lib23.infra_api import Infra
from saas.tools.devops.lib23.deploy_manager_api import DeployManagerApiClient
from saas.tools.devops.lib23.saas_service import SaasService
import saas.tools.devops.lib23.service_topology_tools as stt

from saas.library.python.saas_ctype import SaasCtype
from saas.library.python.awacs import namespace


def update_stage(func):
    """
    Update process stage
    :param func:
    :return:
    """

    def decorator(*args, **kwargs):
        action = func.__name__
        args[0].CURRENT_STAGE = action
        args[0].CURRENT_STAGE_STATE = 'PROCESSING'

        action_result = func(*args, **kwargs)
        if action_result:
            args[0].CURRENT_STAGE_STATE = 'SUCCESS'
        else:
            args[0].CURRENT_STAGE_STATE = 'FAILURE'
        args[0].STAGES_HISTORY.append('%s: %s' % (args[0].CURRENT_STAGE,
                                                  args[0].CURRENT_STAGE_STATE)
                                      )
        return action_result

    return decorator


def read_configuration(config_file):
    """
    Read configuration file in yaml format
    """
    try:
        with open(config_file) as cf:
            config = yaml.load("".join(cf.readlines()))
        return config
    except EnvironmentError:
        logging.critical('Cannot open/parse config file: %s', config_file)


class SaaSServiceManager(object):
    """
    Service management
    """
    CURRENT_STAGE = ''
    CURRENT_STAGE_STATE = ''
    STAGES_HISTORY = []
    ctype_tags = {
        'testing': {},
        'prestable': {'prj': 'saas-prestable', 'nanny': 'OPT_CUSTOM_CTYPE=prestable', 'tvm': '2010902'},
        'prestable_dj': {'prj': 'saas-dj-prestable', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_dj', 'tvm': '2010902'},
        'stable': {'prj': 'saas', 'nanny': 'OPT_CUSTOM_CTYPE=stable', 'tvm': '2011468'},
        'stable_kv': {'prj': 'saas-kv', 'nanny': 'OPT_CUSTOM_CTYPE=stable_kv', 'tvm': '2011468'},
        'stable_middle_kv': {'prj': 'saas-middle-kv', 'nanny': 'OPT_CUSTOM_CTYPE=stable_middle_kv', 'tvm': '2011468'},
        'stable_gemini': {'prj': 'saas-gemini', 'tvm': '2011468'},
        'stable_patents': {'prj': 'saas-patents', 'nanny': 'OPT_CUSTOM_CTYPE=stable_patents', 'tvm': '2011468'},
        'stable_knn': {'prj': 'saas-knn', 'nanny': 'OPT_CUSTOM_CTYPE=stable_knn', 'tvm': '2011468'},
        'stable_dj': {'prj': 'saas-dj', 'nanny': 'OPT_CUSTOM_CTYPE=stable_dj', 'tvm': '2011468'},
        'stable_hamster': {'prj': 'saas-hamster', 'nanny': 'OPT_CUSTOM_CTYPE=stable_hamster', 'tvm': '2011468'},

        'stable_turbo': {'prj': 'saas-turbo', 'nanny': 'OPT_CUSTOM_CTYPE=stable_turbo', 'network_macros': '_SAAS_STABLE_TURBO_BASE_NETS_',
                         'tvm': '2011468'},
        'prestable_turbo': {'prj': 'prestable-turbo', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_turbo',
                            'network_macros': '_SAAS_PRESTABLE_TURBO_BASE_NETS_', 'tvm': '2011468'},

        'stable_ydo': {'prj': 'saas-ydo', 'nanny': 'OPT_CUSTOM_CTYPE=stable_ydo', 'network_macros': '_SAAS_STABLE_YDO_BASE_NETS_',
                       'tvm': '2011468'},
        'prestable_ydo': {'prj': 'prestable-ydo', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_ydo',
                          'network_macros': '_SAAS_PRESTABLE_YDO_BASE_NETS_', 'tvm': '2011468'},
        'stable_hamster_ydo': {'prj': 'saas-hamster-ydo', 'nanny': 'OPT_CUSTOM_CTYPE=stable_hamster_ydo', 'tvm': '2011468'},

        'stable_ymusic': {'prj': 'saas-ymusic', 'nanny': 'OPT_CUSTOM_CTYPE=stable_ymusic',
                          'network_macros': '_SAAS_STABLE_YMUSIC_BASE_NETS_', 'tvm': '2011468'},
        'prestable_ymusic': {'prj': 'prestable-ymusic', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_ymusic',
                             'network_macros': '_SAAS_PRESTABLE_YMUSIC_BASE_NETS_', 'tvm': '2011468'},

        'stable_market': {'prj': 'saas-market', 'nanny': 'OPT_CUSTOM_CTYPE=stable_market', 'tvm': '2011468'},
        'prestable_market': {'prj': 'prestable-market', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_market',
                             'network_macros': '_SAAS_PRESTABLE_MARKET_BASE_NETS_', 'tvm': '2011468'},

        'stable_market_idx': {'prj': 'saas-market_idx', 'nanny': 'OPT_CUSTOM_CTYPE=stable_market_idx',
                              'network_macros': '_SAAS_STABLE_MARKET_IDX_BASE_NETS_', 'tvm': '2011468'},
        'prestable_market_idx': {'prj': 'prestable-market_idx', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_market_idx',
                                 'network_macros': '_SAAS_PRESTABLE_MARKET_IDX_BASE_NETS_', 'tvm': '2011468'},

        'stable_zen': {'prj': 'saas-zen', 'nanny': 'OPT_CUSTOM_CTYPE=stable_zen', 'network_macros': '_SAAS_STABLE_ZEN_BASE_NETS_',
                       'tvm': '2011468'},
        'prestable_zen': {'prj': 'prestable-zen', 'nanny': 'OPT_CUSTOM_CTYPE=prestable_zen',
                          'network_macros': '_SAAS_PRESTABLE_ZEN_BASE_NETS_', 'tvm': '2010902'},

    }

    try:
        all_ctypes = DeployManagerApiClient().ctypes
    except:
        all_ctypes = [
            u'prestable_turbo', u'stable_patents', u'testing', u'prestable', u'prestable_drive', u'prestable_ymusic', u'stable_middle_kv',
            u'prestable_ydo', u'stable_lavka', u'stable_turbo', u'stable_hamster_ymusic', u'stable_dj', u'stable_gemini', u'stable_market', u'stable',
            u'stable_ydo', u'stable_ymusic', u'prestable_zen', u'prestable_market', u'prestable_market_idx', u'stable_drive', u'stable_hamster_ydo',
            u'stable_kv', u'stable_hamster_turbo', u'stable_zen', u'stable_refresh', u'prestable_dj', u'stable_hamster', u'prestable_lavka', u'stable_market_idx'
        ]
        logging.info('Could not obtain ctype list from DM, failing back to static')

    for ctype_name in all_ctypes:
        if ctype_name not in ctype_tags:
            ctype = SaasCtype(ctype_name)
            ctype_tags[ctype.name] = {
                'prj': ctype.yasm_prj_prefix,
                'network_macros': ctype.backend_macro,
                'tvm': ctype.saas_debug_proxy_tvm_id,
            }
            ctype_tags[ctype.name]['nanny'] = 'OPT_CUSTOM_CTYPE=' + ctype_tags[ctype.name]['prj'].replace('-', '_')

    print(ctype_tags)

    def __init__(self, service_name, service_ctype, service_type, instances_count, allocation_type='yp',
                 memory_requirements=10, cpu_requirements=2, sla_info=None, delivery_info='', dc='',
                 prepare_env=False, service_shard_by='', replicas_per_dc=1, tvm_id=None, saas_tvm=False,
                 comment='', startrek_issue=False, skip_dm=False, skip_nanny=False, skip_alloc=False, src_service='',
                 config='conf/ssm.conf', no_indexing=False, flexible_quota=False, skip_awacs=False):

        # Basic parameters
        self.locations = ['SAS', 'MAN', 'VLA']
        self.no_indexing_tag = {
            'itag': ['saas_no_indexing']
        }
        self.nanny_service_tag_regexp = re.compile(r'(?P<name>\w+)@SAAS@')
        # Service variables
        self.__service_name = service_name
        self.__service_name_replaced = self.__service_name.replace('-', '_')
        self.__service_ctype = service_ctype
        self.__service_type = service_type
        self.__allocation_type = allocation_type
        self.__instances_count = int(instances_count)
        self.__replicas_count_per_dc = replicas_per_dc
        self.__memory_requirements = memory_requirements
        self.__cpu_requirements = cpu_requirements
        self.__cpu_requirements_limit = self.__cpu_requirements + 1
        self.__dc = dc
        self.__sla_info = sla_info or {}
        self.__st_ticket = self.__sla_info.get('ticket')
        self.__delivery_info = delivery_info
        self.__prepare_env = prepare_env
        self.__service_shard_by = service_shard_by
        self.__src_service = src_service
        self.__comment = comment
        self.__no_indexing = no_indexing
        self.__startrek_issue = startrek_issue
        self.__shards_count = self.__instances_count / (1 if not self.__replicas_count_per_dc else self.__replicas_count_per_dc)
        self.__network_macros = self.ctype_tags[self.__service_ctype].get('network_macros',
                                                                          '_SAAS_{}_BASE_NETS_'.format(self.__service_ctype.upper()))

        # Skip variables
        self.__skip_alloc = skip_alloc
        self.__skip_awacs = skip_awacs
        self.__skip_nanny = skip_nanny
        self.__skip_dm = skip_dm

        # Tvm settings
        self.__saas_ctype_tvm = self.ctype_tags[self.__service_ctype].get('tvm', '')

        if saas_tvm and tvm_id:
            if type(tvm_id) is list:
                self.__tvm_id = tvm_id
                self.__tvm_id.append(self.__saas_ctype_tvm)
            else:
                self.__tvm_id = [tvm_id, self.__saas_ctype_tvm]
        else:
            self.__tvm_id = tvm_id

        if self.__service_ctype in self.ctype_tags.keys():
            self.__service_ctype_tags = deepcopy(self.ctype_tags[self.__service_ctype])
        else:
            self.__service_ctype_tags = {}

        if self.__service_ctype != 'testing':
            self.__prj_service_tag = '{}-{}'.format(self.__service_ctype_tags['prj'], self.__service_name.replace('_', '-'))

        # Set ferryman name if ferryman preconfigured
        if delivery_info and sla_info:
            if ('ferryman' in delivery_info['delivery_type'] or 'logbroker' in delivery_info['delivery_type']) and not sla_info.get('ferrymans'):
                sla_info['ferrymans'] = [get_ferryman_name(self.__service_name, self.__service_ctype)]

        # Add suffix _h if hamster service / _p for prestable service
        if 'hamster' in self.__service_ctype:
            self.__service_name_replaced += '_h'
        elif self.__service_ctype.startswith('prestable'):
            self.__service_name_replaced += '_p'

        # Instances & clients
        self.__nanny_service_name = 'saas_{}'.format(self.__service_name_replaced.lower())
        self.__NANNY_SERVICE = nanny_api.SaaSNannyService(
            self.__nanny_service_name,
            service_type=self.__service_type,
            service_ctype=self.__service_ctype,
            service_ctype_tag=self.__service_ctype_tags.get('nanny')
        )

        self.__DM_SERVICE = dm_api.DeployManager(
            self.__service_name,
            self.__service_ctype,
            service_type=self.__service_type
        )

        self.__YP_CLIENT = SaaSNannyYpWorkflow(self.__nanny_service_name, self.__NANNY_SERVICE._abc_group, flexible_quota=flexible_quota)

        if self.__startrek_issue:
            self.__STARTREK_CLIENT = startrek_api.SaaSStartrekWorkflow()

    def __str__(self):
        return '\n'.join(self.STAGES_HISTORY)

    @update_stage
    def _create_saas_nanny_service(self):
        """
        Create new SaaS nanny service.
        :return: nanny_api.SaaSNannyService object
        """

        logging.info('Creating new SaaS nanny service %s with ctype %s', self.__nanny_service_name, self.__service_type)

        if self.__NANNY_SERVICE.check_service_exists():
            logging.warning(
                'Service %s already exists. Warning! Next operations can spoil parameters.',
                self.__nanny_service_name
            )
            self.__NANNY_SERVICE.create_service()
        else:
            self.__NANNY_SERVICE.create_service()

        return True

    @update_stage
    def remove_service(self, request_author='saas-robot', message='', ticket=None):
        """
        Remove service from DM, YP and preparing to remove Nanny service.
        """
        common_nanny_services = ['saas_yp_cloud_prestable', 'saas_yp_cloud_base_testing']
        logging.info('Removing service: %s', self.__service_name)
        ss = SaasService(self.__service_ctype, self.__service_name)

        # guess and remove nanny services
        if self.__service_ctype != 'testing':
            logging.info('Guessing nanny service')
            logging.info('Attention! You must SHUTDOWN nanny service before removing')

            nanny_services = [s for s in ss.nanny_services if s.name not in common_nanny_services]
            if len(nanny_services) > 0:
                logging.info('Going to remove nanny services : %s', nanny_services)

                for ns in nanny_services:

                    NANNY_SERVICE = nanny_api.SaaSNannyService(ns.name)
                    backends_type = ns.runtime_attrs['instances']['chosen_type']

                    # Remove snapshots (require for removing yp pod sets)
                    NANNY_SERVICE.remove_service_snapshots()

                    # Remove backends
                    if backends_type == 'YP_POD_IDS' or NANNY_SERVICE.nanny_yp.get_list_pods():
                        logging.info('Removing all yp podsets')
                        self.remove_yp_podsets()

                    # Final remove nanny service
                    NANNY_SERVICE.remove_service()

            else:
                logging.info('No nanny services removed. Services %s will stay alive', ss.nanny_services)

        logging.info('Removing service from DM')
        self.__DM_SERVICE._remove_service()
        namespace.NamespaceManager().clear_service_upstreams(self.__service_ctype, self.__service_name)
        logging.info('Service removed')

    @update_stage
    def remove_yp_podsets(self):
        """
        Remove all yp podsets for service
        """
        for loc in self.locations:
            self.__YP_CLIENT.remove_pods(loc)

    def startrek_workflow(self, action, data=None):
        """
        Startrek actions workflow
        :param action: type str
        :param data: type str
        :return:
        """
        # Create new issue if does not specify other
        if action == 'create' and not self.__st_ticket:
            self.__st_ticket = self.__STARTREK_CLIENT.new_service_request(
                self.__service_name, self.__service_ctype, self.__service_type,
                self.__instances_count, self.__comment, other_data=self.__sla_info
            )
            self.__sla_info['ticket'] = self.__st_ticket

        # Add comment for issue
        if action == 'add_comment' and data:
            self.__STARTREK_CLIENT.add_comment(self.__st_ticket, data)

        # Run progress of issue
        if action == 'run':
            self.__STARTREK_CLIENT.start_progress_issue(self.__st_ticket)

        # Resolve issue
        if action == 'resolve':
            self.__STARTREK_CLIENT.resolve_issue(self.__st_ticket)

        # Close issue
        if action == 'close':
            self.__STARTREK_CLIENT.close_issue(self.__st_ticket)

    def create_testing_service(self):
        """
        Create testing service
        """
        logging.info('Creating testing service %s', self.__service_name)
        if self.__startrek_issue:
            self.startrek_workflow('create')
            self.startrek_workflow('run')

        self.__DM_SERVICE.create_service(self.__instances_count, None,
                                         sla_info=self.__sla_info,
                                         replicas_per_dc=self.__replicas_count_per_dc,
                                         delivery_info=self.__delivery_info,
                                         prepare_env=self.__prepare_env,
                                         tvm_ids=self.__tvm_id,
                                         deploy_proxies=True)

        if self.__startrek_issue:
            print(str(self.__DM_SERVICE).encode('utf-8'))
            self.startrek_workflow('add_comment', data=u'Сервис заведен и будет доступен после выкладки изменений '
                                                       u'на поисковых прокси серверах. \n'
                                                       u'Поддержка: https://wiki.yandex-team.ru/jandekspoisk/saas/Support/\n'
                                                       u'<{cut\n%s\n}>' % str(self.__DM_SERVICE))
            self.startrek_workflow('close')
        logging.info('\n%s', self.__DM_SERVICE)

    def create_production_service(self):
        """
        Create production service
        """
        logging.info('Creating production service %s ctype %s', self.__service_name, self.__service_ctype)

        if self.__startrek_issue:
            self.startrek_workflow('create')
            self.startrek_workflow('run')

        # Prepare nanny service
        if not self.__skip_nanny:
            self._create_saas_nanny_service()
            self.__NANNY_SERVICE._ns.set_infra_notifications(
                service_id=503,
                service_name='SaaS',
                environment_id=Infra().environments_by_name(503)[self.__service_ctype],
                environment_name=self.__service_ctype
            )

        # Prepare service backends
        logging.debug('skip_alloc=%s, skip_nanny=%s', self.__skip_alloc, self.__skip_nanny)
        if not self.__skip_alloc and not self.__skip_nanny:
            self.prepare_yp_backends()

        # Activate basic nanny configuration
        if not self.__skip_nanny:
            self.__NANNY_SERVICE.manage_manual_confirm(False)
            self.__NANNY_SERVICE.manage_degrade_level('1')
            self.__NANNY_SERVICE.snapshot_activate(activation_wait=True)
            self.__NANNY_SERVICE.manage_degrade_level('0.01')

        # Create DM service
        if not self.__skip_dm:
            self.__DM_SERVICE.create_service(
                self.__instances_count,
                self.__nanny_service_name,
                sla_info=self.__sla_info,
                delivery_info=self.__delivery_info,
                prepare_env=self.__prepare_env,
                replicas_per_dc=self.__replicas_count_per_dc,
                src_service=self.__src_service,
                shard_by=self.__service_shard_by,
                tvm_ids=self.__tvm_id)
            if self.__startrek_issue:
                self.startrek_workflow('add_comment', data=u'Сервис заведен и будет доступен после выкладки изменений '
                                                           u'на поисковых прокси серверах.\n'
                                                           u'Поддержка: https://wiki.yandex-team.ru/jandekspoisk/saas/Support/\n'
                                                           u'Важно! Перед запуском сервиса в production обязательно следующее:\n'
                                                           u'1) Проведение нагрузочного тестирования поисковыми запросами;\n'
                                                           u'2) Предупредить дежурного по сервисным поискам SaaS о запуске в production.\n'
                                                           u'\n<{cut\n%s\n}>\n' % str(self.__DM_SERVICE))

                self.startrek_workflow('resolve')
            logging.info('\n%s' % self.__DM_SERVICE)

        if not self.__skip_awacs:
            namespace.NamespaceManager().set_upstreams_for_service(self.__service_name, self.__service_ctype, timeout_from_sla=True)

        if '_dj' in self.__service_ctype or self.__service_type == 'dj':
            self.patch_dj_service()

    @update_stage
    def patch_dj_service(self):
        return SaasDJService(self.__service_ctype, self.__service_name).post_create_hook()

    def prepare_yp_backends(self):
        """
        1) Configure volumes data
        2) Allocate pods with volumes
        3) Configure instances on nanny service if skip_nanny is not choiced
        :return:
        """
        # Prepare volumes
        self.__YP_CLIENT.add_persistent_volume(
            '/logs', 'hdd', stt.calculate_logs_volume_size(int(self.__sla_info.get('search_rps', 10)),
                                                           self.__replicas_count_per_dc * 3) * 1024
        )
        self.__YP_CLIENT.add_persistent_volume(
            '/cores', 'hdd', stt.calculate_cores_volume_size(self.__memory_requirements * 2 ** 30) * 1024
        )
        self.__YP_CLIENT.add_persistent_volume(
        #    '/data', 'ssd', stt.calculate_data_volume_size(int(self.__sla_info.get('total_index_size_bytes', 0)), self.__shards_count) * 1024)
            '/data',
            'lvm',
            stt.calculate_data_volume_size(int(self.__sla_info.get('total_index_size_bytes', 0)), self.__shards_count) * 1024,
            disk_id='data'
        )
        self.__YP_CLIENT.add_persistent_volume('/state', 'hdd', 1024)

        # Pods allocation
        self.__YP_CLIENT.allocate_pods(self.__cpu_requirements * 1000, self.__memory_requirements * 1024, 1024, root_storage_type='hdd',
                                       cpu_limit=self.__cpu_requirements_limit * 1000, instances_count=self.__instances_count,
                                       network_macros=self.__network_macros, hr_pod_names=(len(self.__nanny_service_name) <= 32))

        sandbox_resources = [{
            'resource_type': 'RTYSERVER_LOOP_CONF_YP',
            'task_type': 'BUILD_RTYSERVER_CONFIG',
            'local_path': 'loop.conf'
        }]
        self.__NANNY_SERVICE.update_service_resources(sandbox_resources=sandbox_resources)

        # Add pods to nanny service
        self.__NANNY_SERVICE.configure_yp_instances(tags={'metaprj': 'unknown',
                                                          'itype': 'rtyserver',
                                                          'ctype': 'prestable' if 'prestable' in self.__service_ctype else 'prod',
                                                          'prj': self.__prj_service_tag})
        self.__NANNY_SERVICE.add_env_variable('saas_daemon', 'CUSTOM_CTYPE', self.__service_ctype)
        self.__NANNY_SERVICE.add_env_variable('pushclient', 'CUSTOM_CTYPE', self.__service_ctype)
        self.__NANNY_SERVICE.nanny_replication.update_policy()


def prepare_logging(options):
    """
    Prepare logging settings
    :param options: argparse.Namespace
    """
    # basedir = os.path.abspath(__file__).split(os.path.basename(__file__))[0]
    basedir = os.getcwd()
    logdir = '%s/logs' % basedir
    if not os.path.exists(logdir):
        os.makedirs(logdir)

    if options.debug:
        level = logging.DEBUG
    else:
        level = logging.INFO

    logging.basicConfig(
        level=level,
        format='%(asctime)s | %(filename)18s#%(lineno)s | %(levelname)-5s | %(message)s',
        datefmt='%H:%M:%S',
        filename='logs/SaaSServiceManager.log')

    console = logging.StreamHandler()
    console.addFilter(logging.Filter(name='root'))
    console.setFormatter(logging.Formatter('[%(module)18s][%(levelname)-5s] %(message)-20s '))
    console.setLevel(level)
    logging.getLogger('').addHandler(console)


def parse_args(*args):
    options = argparse.ArgumentParser(prog='SaaSServiceManager',
                                      formatter_class=argparse.RawTextHelpFormatter,
                                      description="""
DESCRIPTION
Create and manage SaaS services using NannyServicesAPI.
Requires environment variables OAUTH_SANDBOX, NANNY_OAUTH_TOKEN, STARTREK_OAUTH_TOKEN for correct
working with Sandbox, Nanny and Startrek services.
Cases:
  1) Create new SaaS service for prestable/testing or any of production ctypes.
  2) Create new nanny service with production SaaS environment.
""")

    options.add_argument('service_name', type=str,
                         help='Specifies service name')
    options.add_argument('-m', '--memory', dest='memory', default=10, type=int,
                         help='Memory requirements. Default value: 10 Gb')
    options.add_argument('-c', '--instances-count', default=1, required='--remove' not in sys.argv,
                         dest='instances_count',
                         type=int, help='Total instances count per location')
    options.add_argument('--dc', dest='dc', default='',
                         help='Specify DC for creating group or allocation')
    options.add_argument('--cpu', dest='cpu_req', default=2, type=int,
                         help="""Specify cpu requirements. Number of cores. Default value: 2
                         """)
    options.add_argument('--ctype', dest='ctype', required=True, choices=SaaSServiceManager.ctype_tags.keys(),
                         help='Sets ctype of service'),
    options.add_argument('--replicas-per-dc', dest='replicas_per_dc', type=int, default=1,
                         help="""Create more than one replica per location""")
    options.add_argument('--key-value', dest='kv_service', action='store_true',
                         help="""Create KV service with rtyserver_lf binary with default kv
                              configuration""")
    options.add_argument('--search', dest='search_service', action='store_true',
                         help="""Create search service with rtyserver binary and default search
                         configuration""")
    options.add_argument('--env', dest='environment', action='store_true',
                         help="""Force prepare basic key-value or search environment on DM service.
Requires additional option --key-value or --search for specify type of service""")
    options.add_argument('--keyprefix', dest='keyprefix', action='store_true',
                         help='Sets shard_by=keyprefix  to DM service')
    options.add_argument('--template', dest='template_service',
                         help='Copy DM configuration from specified template service')
    options.add_argument('--remove', dest='remove_service', action='store_true',
                         help="""Remove all instances from nanny service. Prepare to remove service""")
    options.add_argument('--debug', dest='debug', action='store_true', default=False,
                         help='Enable debug mode with additional information')
    options.add_argument('--skip-alloc', dest='skip_alloc', action='store_true', default=False,
                         help='Skip creating allocations (yp pods) for specified service')
    options.add_argument('--skip-awacs', dest='skip_awacs', action='store_true', default=False,
                         help='Skip creating upstream in common and separated balancer (if possible)')
    options.add_argument('--skip-nanny', dest='skip_nanny', action='store_true', default=False,
                         help='Skip creating nanny service group for specified service')
    options.add_argument('--skip-dm', dest='skip_dm', action='store_true', default=False,
                         help='Skip creating DM group for specified service')
    options.add_argument('--startrek', dest='startrek', action='store_true', default=False,
                         help='Create startrek ticket')
    options.add_argument('--tvm-id', dest='tvm_id', type=str,
                         help="Configure service searchproxy configuration with specified tvm_id")
    options.add_argument('--allow-saas-tvm', dest='saas_tvm', action='store_true', default=False,
                         help="Configure service searchproxy configuration SaaS tvm_id")
    options.add_argument('--allow_tmp_quota', dest='yp_flexible_quota', action='store_true', default=False,
                         help="Make fall back on temporary quota if saas quota is unavailable")

    # Yt options
    options.add_argument('--yt-cluster', dest='yt_cluster', default='hahn',
                         choices=['hahn', 'arnold'],
                         help='Choice yt cluster for service configuration. Default: hahn')
    options.add_argument('--yt-mr', dest='yt_path', default='',
                         help='Choice ytpull MapReduce configuration.')
    options.add_argument('--yt-snapshot', dest='yt_snapshot_path', default='',
                         help='Choice ytpull Snapshot configuration.')
    options.add_argument('--ferryman-mr', dest='ferryman_mr', default=False, action='store_true',
                         help='Choice Ferryman MapReduce configuration.')
    options.add_argument('--ferryman-snapshot', dest='ferryman_snapshot', default=False, action='store_true',
                         help='Choice Ferryman Snapshot configuration.')
    options.add_argument('--ferryman-delta', dest='ferryman_delta', default=False, action='store_true',
                         help='Choice Ferryman Delta configuration.')
    options.add_argument('--logbroker', dest='logbroker', default=False, action='store_true',
                         help='Choice Logbroker(PQ) configuration.')
    options.add_argument('--yt-token', dest='yt_token', default='',
                         help='Sets ytpull token.')

    # Service SLA paramaters
    options.add_argument('--owners', dest='owners', default='bvdvlg', required='--remove' not in sys.argv,
                         help='Sets service owners')
    options.add_argument('--resps', dest='resps', default='', help='Sets service resps')
    options.add_argument('--ticket', dest='ticket', default='', help='Sets service startrek ticket')
    options.add_argument('--ferryman', dest='ferryman', default='', help='Sets service ferryman nanny services')

    options.add_argument('--search-rps', dest='search_rps', default=1,
                         help='Sets planned search RPS')
    options.add_argument('--index-size', dest='index_size', default=1073741824,
                         help='Sets planned index size in bytes')
    options.add_argument('--search-99', dest='search_99',
                         help='Sets planned search 99 quantile')
    options.add_argument('--search-999', dest='search_999',
                         help='Sets planned search 999 quantile')
    options.add_argument('--max-docs', dest='max_docs',
                         help='Sets service max docs count')
    options.add_argument('--abc-user-service', dest='abc_user_service', default=664,
                         help='Sets ABC user service')
    options.add_argument('--abc-quota-service', dest='abc_quota_service', default=664,
                         help='Sets ABC quota service')
    options.add_argument('--comment', dest='comment', default='', help='Comment with additional details of service')

    if args:
        opts = options.parse_args(args)
    else:
        opts = options.parse_args()

    return opts


def main(*args):
    """
    Cases:
    1) Create allocation for saas_cloud_base_prestable.
    2) Create new nanny service with allocation with prestable SaaS environment.
    3) Create new nanny service with production SaaS environment.
    """
    options = parse_args(*args)
    # Logging
    prepare_logging(options)

    # Load resources (required with arcadia building)
    load_and_write_resources('/tmpl')
    load_and_write_resources('/conf')

    # Service type for Deploy Manager
    if options.kv_service:
        dm_service_type = 'kv'
    elif options.search_service:
        dm_service_type = 'search'
    else:
        dm_service_type = ''

    if options.keyprefix:
        shard_by = 'keyprefix'
    else:
        shard_by = 'url_hash'

    if options.dc:
        options.dc = options.dc.split(',')

    sla_data = {
        'owners': options.owners.split(','),
        'responsibles': options.resps.split(','),
        'ticket': options.ticket,
        'ferrymans': options.ferryman,
        'search_rps': options.search_rps,
        'search_rps_planned': options.search_rps,
        'total_index_size_bytes': options.index_size,
        'search_q_99_ms': options.search_99,
        'search_q_999_ms': options.search_999,
        'maxdocs': options.max_docs,
        'abc_user_service': options.abc_user_service,
        'abc_quota_service': options.abc_quota_service
    }

    yt_path = ''
    delivery_type = ''
    if options.yt_path:
        yt_path = options.yt_path
        delivery_type = 'mapreduce'
    if options.yt_snapshot_path:
        yt_path = options.yt_snapshot_path
        delivery_type = 'snapshot'
    if options.ferryman_mr:
        delivery_type = 'ferryman_mapreduce'
    if options.ferryman_snapshot:
        delivery_type = 'ferryman_snapshot'
    if options.ferryman_delta:
        delivery_type = 'ferryman_delta'
    if options.logbroker:
        delivery_type = 'logbroker'

    if delivery_type:
        delivery_info = {
            'yt_cluster': options.yt_cluster,
            'yt_path': yt_path,
            'delivery_type': delivery_type,
            'yt_token': options.yt_token
        }
    else:
        delivery_info = None

    manager = SaaSServiceManager(options.service_name, options.ctype, dm_service_type,
                                 options.instances_count,
                                 memory_requirements=options.memory,
                                 dc=options.dc,
                                 sla_info=sla_data,
                                 delivery_info=delivery_info,
                                 cpu_requirements=options.cpu_req,
                                 prepare_env=options.environment,
                                 service_shard_by=shard_by,
                                 skip_dm=options.skip_dm,
                                 replicas_per_dc=options.replicas_per_dc,
                                 startrek_issue=options.startrek,
                                 skip_nanny=options.skip_nanny,
                                 skip_alloc=options.skip_alloc,
                                 src_service=options.template_service,
                                 tvm_id=options.tvm_id,
                                 saas_tvm=options.saas_tvm,
                                 comment=options.comment,
                                 flexible_quota=options.yp_flexible_quota,
                                 skip_awacs=options.skip_awacs)
    # Cases
    if options.remove_service:
        current_user = os.getlogin()  # some alternative ?
        manager.remove_service(request_author=current_user, message=options.comment, ticket=options.ticket)
    elif options.ctype == 'testing':
        manager.create_testing_service()
    else:
        manager.create_production_service()


if __name__ == '__main__':
    main()
