コード例 #1
0
class ServiceOffersCommand(CloudClientCommand):
    DEFAULT_TIMEOUT = 600
    EXCHANGE_RATES_SERVICE_URL = 'https://api.exchangeratesapi.io/latest'

    RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES = 'serviceAttributeNamespaces'

    DRY_RUN_KEY = 'dry-run'
    COUNTRY_KEY = 'country'
    SS_ENDPOINT_KEY = 'ss-url'
    SS_USERNAME_KEY = 'ss-user'
    SS_PASSWORD_KEY = 'ss-pass'
    BASE_CURRENCY_KEY = 'currency'
    CONNECTOR_NAME_KEY = 'connector-name'

    def __init__(self):
        super(ServiceOffersCommand, self).__init__()

        self.cc = None
        self.ssapi = None
        self.base_currency = 'EUR'
        self._exchange_rates = {}

    def _initialize(self):
        """
        This method is called once command arguments have been parsed and self.cc and self.ssapi are available
        """
        pass

    def _get_default_timeout(self):
        return self.DEFAULT_TIMEOUT

    def _list_vm_sizes(self):
        """
        Return a list of available VM sizes.
        """
        return self.cc._list_vm_sizes() if self.cc else None

    def _get_cpu(self, vm_size):
        """
        Extract and return the amount of vCPU from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_cpu(vm_size) if self.cc else None

    def _get_ram(self, vm_size):
        """
        Extract and return the size of the RAM memory in MB from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_ram(vm_size) if self.cc else None

    def _get_disk(self, vm_size):
        """
        Extract and return the size of the root disk in GB from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype float
        """
        return self.cc._size_get_disk(vm_size) if self.cc else None

    def _get_instance_type(self, vm_size):
        """
        Extract and return the instance type from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_instance_type(vm_size) if self.cc else None

    def _get_country(self):
        """
        Return the 2-letters symbol of the country where the Cloud reside.
        """
        return self.get_option(self.COUNTRY_KEY)

    def _get_supported_os(self, vm_size):
        """
        Return a list of supported OS for the specified vm_size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return ['linux', 'windows']

    def _get_price(self, vm_size, os, root_disk_size=None):
        """
        Get the price for a give vm_size, OS and optionnal root disk size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        :param os: The name of the operating system type (eg: 'linux', 'suse', 'windows')
        :param root_disk_size: The size of the root disk in GB
        :return: A tuple containing the price per hour and the currency. eg:(0.24, 'USD') )
        """
        return None, None

    def _get_root_disk_sizes(self, vm_size, os):
        """
        Return a list of available root disk sizes for the given vm_size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        :param os: The name of the operating system type (eg: 'linux', 'suse', 'windows')
        :return: A list of available disk sizes
        """
        disk_size = self._get_disk(vm_size)
        if disk_size is not None and disk_size > 0:
            return [disk_size]

        return [10, 25, 50, 100, 200, 400, 600, 800, 1000, 1600, 2000, 4000, 6000, 8000, 10000]

    def _get_root_disk_type(self, vm_size):
        """
        Return the type of the root disk (eg: HDD, SSD, EBS, ...)
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return 'Unknown'

    def _get_billing_unit(self, vm_size):
        """
        Return the billing period
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return 'MIN'

    def _get_platform(self, vm_size):
        """
        Return the name of platform
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        pass

    def _get_prefix(self):
        """
        Return the prefix (namespace) to use for extra attributes
        :rtype: str
        """
        pass

    def _get_extra_attributes(self, vm_size):
        """
        Return the billing period
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        pass

    def get_exchange_rate(self, src_currency, dst_currency):
        if dst_currency not in self._exchange_rates:
            self._exchange_rates[dst_currency] = requests.get(self.EXCHANGE_RATES_SERVICE_URL,
                                                              params={'base': dst_currency}).json().get('rates', {})
        return 1.0 / self._exchange_rates.get(dst_currency, {}).get(src_currency)

    def convert_currency(self, src_currency, dst_currency, amount):
        return amount * self.get_exchange_rate(src_currency, dst_currency) if src_currency != dst_currency else amount

    @staticmethod
    def _generate_service_attribute_namespace(prefix, description=None, acl=None):

        if acl is None:
            acl = {
                "owner": {
                    "principal": "ADMIN",
                    "type": "ROLE"
                },
                "rules": [{
                    "principal": "USER",
                    "type": "ROLE",
                    "right": "VIEW"
                }, {
                    "type": "ROLE",
                    "principal": "ADMIN",
                    "right": "ALL"
                }]
            }

        san = {
            "prefix": prefix,
            "id": "service-attribute-namespace/" + prefix,
            "acl": acl,
            "resourceURI": "http://sixsq.com/slipstream/1/ServiceAttributeNamespace",
            "uri": "http://sixsq.com/slipstream/schema/1/" + prefix
        }

        if description is not None:
            san['description'] = description

        return san

    def _add_service_attribute_namespace_if_not_exist(self, prefix, description=None, acl=None):
        verbose = self.get_option('verbose')

        cimi_resp = self.ssapi.cimi_search(self.RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES,
                                           filter='prefix="{0}"'.format(prefix))

        if cimi_resp.count == 0:
            service_attribute_namespace = self._generate_service_attribute_namespace(prefix, description, acl)
            if verbose:
                print('\nAddinging the following service attribute namespace {0} ...\n{1}'
                      .format(prefix, service_attribute_namespace))
            self.ssapi.cimi_add(self.RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES, service_attribute_namespace)

    @staticmethod
    def _generate_service_offer(connector_instance_name, cpu, ram, root_disk, root_disk_type, os, price,
                                instance_type=None, base_currency='EUR', billing_unit='MIN', country=None,
                                platform=None, prefix=None, extra_attributes=None):
        resource_type = 'VM'
        resource_class = 'standard'

        instance_type = instance_type or ''
        instance_type_in_name = ' {0}'.format(instance_type) if instance_type else ''
        instance_type_in_description = ' ({0})'.format(instance_type) if instance_type else ''

        service_offer = {
            "name": "({0:d}/{1:d}/{2:d}{3} {4}) [{5}]".format(cpu, ram, root_disk, instance_type_in_name, os, country),
            "description": "{0} ({1}) with {2:d} vCPU, {3:d} MiB RAM, {4:d} GiB root disk, {5} [{6}]{7}"
                .format(resource_type, resource_class, cpu, ram, root_disk, os, country, instance_type_in_description),
            "resource:vcpu": cpu,
            "resource:ram": ram,
            "resource:disk": root_disk,
            "resource:diskType": root_disk_type,
            "resource:type": resource_type,
            "resource:class": resource_class,
            "resource:country": country,
            "resource:platform": platform,
            "resource:operatingSystem": os,
            "resource:instanceType": instance_type,
            "price:unitCost": price,  # price  price:currency/price:unitcode
            "price:unitCode": "HUR",
            "price:freeUnits": 0,
            "price:currency": base_currency,
            "price:billingUnitCode": billing_unit,  # Minimum time quantum for a resource
            "price:billingPeriodCode": "MON",  # A bill is sent every billingPeriodCode
            "connector": {"href": connector_instance_name},
            "acl": {
                "owner": {
                    "type": "ROLE",
                    "principal": "ADMIN"
                },
                "rules": [{
                    "principal": "USER",
                    "right": "VIEW",
                    "type": "ROLE"
                }, {
                    "principal": "ADMIN",
                    "right": "ALL",
                    "type": "ROLE"
                }]
            },
        }
        if extra_attributes:
            if not prefix:
                raise ValueError('A prefix has to be defined with _get_prefix() to specify extra_attributes')
            for k, v in extra_attributes.items():
                service_offer['{0}:{1}'.format(prefix, k)] = v

        return service_offer

    def _generate_service_offers(self, connector_instance_name):
        service_offers = []

        for vm_size in self._list_vm_sizes():
            cpu = int(self._get_cpu(vm_size))
            ram = int(self._get_ram(vm_size))
            root_disk_type = self._get_root_disk_type(vm_size)
            instance_type = self._get_instance_type(vm_size)
            billing_unit = self._get_billing_unit(vm_size)
            platform = self._get_platform(vm_size)
            country = self._get_country()
            prefix = self._get_prefix()
            extra_attributes = self._get_extra_attributes(vm_size)

            if not platform and self.cc:
                platform = self.cc.cloudName

            for os in self._get_supported_os(vm_size):
                for root_disk in self._get_root_disk_sizes(vm_size, os):
                    price = None
                    raw_price, currency = self._get_price(vm_size, os, root_disk)
                    if raw_price is not None:
                        price = self.convert_currency(currency, self.base_currency, raw_price)

                    service_offers.append(self._generate_service_offer(connector_instance_name, cpu, ram, root_disk,
                                                                       root_disk_type, os, price, instance_type,
                                                                       self.base_currency, billing_unit, country,
                                                                       platform, prefix, extra_attributes))
        return service_offers

    def do_work(self):
        ch = ConfigHolder(options={'verboseLevel': 0,
                                   'retry': False,
                                   KEY_RUN_CATEGORY: ''},
                          context={'foo': 'bar'})
        self.cc = self.get_connector_class()(ch)
        self.cc._initialization(self.user_info, **self.get_initialization_extra_kwargs())

        self.base_currency = self.get_option(self.BASE_CURRENCY_KEY)

        verbose = self.get_option('verbose')
        dry_run = self.get_option(self.DRY_RUN_KEY)
        ss_endpoint = self.get_option(self.SS_ENDPOINT_KEY)
        ss_username = self.get_option(self.SS_USERNAME_KEY)
        ss_password = self.get_option(self.SS_PASSWORD_KEY)
        connector_instance_name = self.get_option(self.CONNECTOR_NAME_KEY)

        filter_connector_vm = ' and '.join(['connector/href="{0}"'.format(connector_instance_name),
                                            'resource:type="VM"'])

        self.ssapi = Api(endpoint=ss_endpoint, cookie_file=None, insecure=True)
        if not dry_run:
            self.ssapi.login_internal(ss_username, ss_password)

        self._initialize()

        service_offers = self._generate_service_offers(connector_instance_name)

        if not service_offers:
            raise RuntimeError("No service offer found")

        if not dry_run and service_offers:
            self._add_service_attribute_namespace_if_not_exist('resource')
            self._add_service_attribute_namespace_if_not_exist('price')
            prefix = self._get_prefix()
            if prefix:
                self._add_service_attribute_namespace_if_not_exist(prefix)

        service_offers_ids = set()

        for service_offer in service_offers:
            if dry_run:
                print('\nService offer {0}:\n{1}'.format(service_offer['name'], service_offer))
            else:
                cimi_filter = \
                    ' and '.join([filter_connector_vm,
                                  'resource:class="{0}"'.format(service_offer['resource:class']),
                                  'resource:vcpu={0}'.format(service_offer['resource:vcpu']),
                                  'resource:ram={0}'.format(service_offer['resource:ram']),
                                  'resource:disk={0}'.format(service_offer['resource:disk']),
                                  'resource:operatingSystem="{0}"'.format(service_offer['resource:operatingSystem']),
                                  'resource:country="{0}"'.format(service_offer['resource:country']),
                                  'resource:instanceType="{0}"'.format(service_offer['resource:instanceType'])])

                search_result = self.ssapi.cimi_search('serviceOffers', filter=cimi_filter)
                result_list = search_result.resources_list
                result_count = len(result_list)

                if result_count == 0:
                    if verbose:
                        print('\nAddinging the following service offer {0} to {1}...\n{2}'.format(service_offer['name'],
                                                                                                  ss_endpoint,
                                                                                                  service_offer))

                    response = self.ssapi.cimi_add('serviceOffers', service_offer)
                    service_offers_ids.add(response.json['resource-id'])
                elif result_count == 1:
                    if verbose:
                        print('\nUpdating the following service offer {0} to {1}...\n{2}'.format(service_offer['name'],
                                                                                                 ss_endpoint,
                                                                                                 service_offer))

                    response = self.ssapi.cimi_edit(result_list[0].id, service_offer)
                    service_offers_ids.add(response.id)
                else:
                    print('\n!!! Warning duplicates found of following service offer on {0} !!!\n{1}'
                          .format(ss_endpoint, service_offer['name']))
                    for result in result_list:
                        service_offers_ids.add(result.id)

        if not dry_run:
            response = self.ssapi.cimi_search('serviceOffers', filter=filter_connector_vm)
            old_service_offers_ids = set(r.id for r in response.resources())
            service_offers_ids_to_delete = old_service_offers_ids - service_offers_ids

            for id in service_offers_ids_to_delete:
                if verbose:
                    offer = self.ssapi.cimi_get(id)
                    print('\nDeleting the following service offer with id {0}...\n{1}'.format(id, offer.json))

                self.ssapi.cimi_delete(id)

        print('\n\nCongratulation, executon completed.')

    def _set_command_specific_options(self, parser):
        parser.add_option('--' + self.BASE_CURRENCY_KEY, dest=self.BASE_CURRENCY_KEY,
                          help='Currency to use', default='EUR', metavar='CURRENCY')

        parser.add_option('--' + self.COUNTRY_KEY, dest=self.COUNTRY_KEY,
                          help='Country where the Cloud reside', default='Unknown', metavar='COUNTRY')

        parser.add_option('--' + self.CONNECTOR_NAME_KEY, dest=self.CONNECTOR_NAME_KEY,
                          help='Connector instance name to be used as a connector href for service offers',
                          default=None, metavar='CONNECTOR_NAME')

        parser.add_option('--' + self.SS_ENDPOINT_KEY, dest=self.SS_ENDPOINT_KEY,
                          help='SlipStream endpoint used where the service offers are pushed to. ' +
                               '(default: https://nuv.la)',
                          default='https://nuv.la', metavar='URL')

        parser.add_option('--' + self.SS_USERNAME_KEY, dest=self.SS_USERNAME_KEY,
                          help='Username to be used on SlipStream Endpoint',
                          default=None, metavar='USERNAME')

        parser.add_option('--' + self.SS_PASSWORD_KEY, dest=self.SS_PASSWORD_KEY,
                          help='Password to be used on SlipStream Endpoint',
                          default=None, metavar='PASSWORD')

        parser.add_option('--' + self.DRY_RUN_KEY, dest=self.DRY_RUN_KEY,
                          help='Just print service offers to stdout and exit',
                          action='store_true')

    def _get_command_mandatory_options(self):
        return [self.CONNECTOR_NAME_KEY]
コード例 #2
0
class ServiceOffersCommand(CloudClientCommand):
    DEFAULT_TIMEOUT = 600
    EXCHANGE_RATES_SERVICE_URL = 'https://api.exchangeratesapi.io/latest'

    RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES = 'serviceAttributeNamespaces'

    DRY_RUN_KEY = 'dry-run'
    COUNTRY_KEY = 'country'
    SS_ENDPOINT_KEY = 'ss-url'
    SS_USERNAME_KEY = 'ss-user'
    SS_PASSWORD_KEY = 'ss-pass'
    BASE_CURRENCY_KEY = 'currency'
    CONNECTOR_NAME_KEY = 'connector-name'

    def __init__(self):
        super(ServiceOffersCommand, self).__init__()

        self.cc = None
        self.ssapi = None
        self.base_currency = 'EUR'
        self._exchange_rates = {}

    def _initialize(self):
        """
        This method is called once command arguments have been parsed and self.cc and self.ssapi are available
        """
        pass

    def _get_default_timeout(self):
        return self.DEFAULT_TIMEOUT

    def _list_vm_sizes(self):
        """
        Return a list of available VM sizes.
        """
        return self.cc._list_vm_sizes() if self.cc else None

    def _get_cpu(self, vm_size):
        """
        Extract and return the amount of vCPU from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_cpu(vm_size) if self.cc else None

    def _get_ram(self, vm_size):
        """
        Extract and return the size of the RAM memory in MB from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_ram(vm_size) if self.cc else None

    def _get_disk(self, vm_size):
        """
        Extract and return the size of the root disk in GB from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype float
        """
        return self.cc._size_get_disk(vm_size) if self.cc else None

    def _get_instance_type(self, vm_size):
        """
        Extract and return the instance type from the specified vm_size.
        :param vm_size: A 'size' object as in the list returned by _list_vm_sizes().
        :rtype int
        """
        return self.cc._size_get_instance_type(vm_size) if self.cc else None

    def _get_country(self):
        """
        Return the 2-letters symbol of the country where the Cloud reside.
        """
        return self.get_option(self.COUNTRY_KEY)

    def _get_supported_os(self, vm_size):
        """
        Return a list of supported OS for the specified vm_size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return ['linux', 'windows']

    def _get_price(self, vm_size, os, root_disk_size=None):
        """
        Get the price for a give vm_size, OS and optionnal root disk size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        :param os: The name of the operating system type (eg: 'linux', 'suse', 'windows')
        :param root_disk_size: The size of the root disk in GB
        :return: A tuple containing the price per hour and the currency. eg:(0.24, 'USD') )
        """
        return None, None

    def _get_root_disk_sizes(self, vm_size, os):
        """
        Return a list of available root disk sizes for the given vm_size
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        :param os: The name of the operating system type (eg: 'linux', 'suse', 'windows')
        :return: A list of available disk sizes
        """
        disk_size = self._get_disk(vm_size)
        if disk_size is not None and disk_size > 0:
            return [disk_size]

        return [
            10, 25, 50, 100, 200, 400, 600, 800, 1000, 1600, 2000, 4000, 6000,
            8000, 10000
        ]

    def _get_root_disk_type(self, vm_size):
        """
        Return the type of the root disk (eg: HDD, SSD, EBS, ...)
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return 'Unknown'

    def _get_billing_unit(self, vm_size):
        """
        Return the billing period
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        return 'MIN'

    def _get_platform(self, vm_size):
        """
        Return the name of platform
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        pass

    def _get_prefix(self):
        """
        Return the prefix (namespace) to use for extra attributes
        :rtype: str
        """
        pass

    def _get_extra_attributes(self, vm_size):
        """
        Return the billing period
        :param vm_size: A vm_size object as returned by the method _list_vm_sizes() of the connector
        """
        pass

    def get_exchange_rate(self, src_currency, dst_currency):
        if dst_currency not in self._exchange_rates:
            self._exchange_rates[dst_currency] = requests.get(
                self.EXCHANGE_RATES_SERVICE_URL, params={
                    'base': dst_currency
                }).json().get('rates', {})
        return 1.0 / self._exchange_rates.get(dst_currency,
                                              {}).get(src_currency)

    def convert_currency(self, src_currency, dst_currency, amount):
        return amount * self.get_exchange_rate(
            src_currency,
            dst_currency) if src_currency != dst_currency else amount

    @staticmethod
    def _generate_service_attribute_namespace(prefix,
                                              description=None,
                                              acl=None):

        if acl is None:
            acl = {
                "owner": {
                    "principal": "ADMIN",
                    "type": "ROLE"
                },
                "rules": [{
                    "principal": "USER",
                    "type": "ROLE",
                    "right": "VIEW"
                }, {
                    "type": "ROLE",
                    "principal": "ADMIN",
                    "right": "ALL"
                }]
            }

        san = {
            "prefix": prefix,
            "id": "service-attribute-namespace/" + prefix,
            "acl": acl,
            "resourceURI":
            "http://sixsq.com/slipstream/1/ServiceAttributeNamespace",
            "uri": "http://sixsq.com/slipstream/schema/1/" + prefix
        }

        if description is not None:
            san['description'] = description

        return san

    def _add_service_attribute_namespace_if_not_exist(self,
                                                      prefix,
                                                      description=None,
                                                      acl=None):
        verbose = self.get_option('verbose')

        cimi_resp = self.ssapi.cimi_search(
            self.RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES,
            filter='prefix="{0}"'.format(prefix))

        if cimi_resp.count == 0:
            service_attribute_namespace = self._generate_service_attribute_namespace(
                prefix, description, acl)
            if verbose:
                print(
                    '\nAddinging the following service attribute namespace {0} ...\n{1}'
                    .format(prefix, service_attribute_namespace))
            self.ssapi.cimi_add(self.RESOURCE_SERVICE_ATTRIBUTE_NAMESPACES,
                                service_attribute_namespace)

    @staticmethod
    def _generate_service_offer(connector_instance_name,
                                cpu,
                                ram,
                                root_disk,
                                root_disk_type,
                                os,
                                price,
                                instance_type=None,
                                base_currency='EUR',
                                billing_unit='MIN',
                                country=None,
                                platform=None,
                                prefix=None,
                                extra_attributes=None):
        resource_type = 'VM'
        resource_class = 'standard'

        instance_type = instance_type or ''
        instance_type_in_name = ' {0}'.format(
            instance_type) if instance_type else ''
        instance_type_in_description = ' ({0})'.format(
            instance_type) if instance_type else ''

        service_offer = {
            "name":
            "({0:d}/{1:d}/{2:d}{3} {4}) [{5}]".format(cpu, ram, root_disk,
                                                      instance_type_in_name,
                                                      os, country),
            "description":
            "{0} ({1}) with {2:d} vCPU, {3:d} MiB RAM, {4:d} GiB root disk, {5} [{6}]{7}"
            .format(resource_type, resource_class, cpu, ram, root_disk, os,
                    country, instance_type_in_description),
            "resource:vcpu":
            cpu,
            "resource:ram":
            ram,
            "resource:disk":
            root_disk,
            "resource:diskType":
            root_disk_type,
            "resource:type":
            resource_type,
            "resource:class":
            resource_class,
            "resource:country":
            country,
            "resource:platform":
            platform,
            "resource:operatingSystem":
            os,
            "resource:instanceType":
            instance_type,
            "price:unitCost":
            price,  # price  price:currency/price:unitcode
            "price:unitCode":
            "HUR",
            "price:freeUnits":
            0,
            "price:currency":
            base_currency,
            "price:billingUnitCode":
            billing_unit,  # Minimum time quantum for a resource
            "price:billingPeriodCode":
            "MON",  # A bill is sent every billingPeriodCode
            "connector": {
                "href": connector_instance_name
            },
            "acl": {
                "owner": {
                    "type": "ROLE",
                    "principal": "ADMIN"
                },
                "rules": [{
                    "principal": "USER",
                    "right": "VIEW",
                    "type": "ROLE"
                }, {
                    "principal": "ADMIN",
                    "right": "ALL",
                    "type": "ROLE"
                }]
            },
        }
        if extra_attributes:
            if not prefix:
                raise ValueError(
                    'A prefix has to be defined with _get_prefix() to specify extra_attributes'
                )
            for k, v in extra_attributes.items():
                service_offer['{0}:{1}'.format(prefix, k)] = v

        return service_offer

    def _generate_service_offers(self, connector_instance_name):
        service_offers = []

        for vm_size in self._list_vm_sizes():
            cpu = int(self._get_cpu(vm_size))
            ram = int(self._get_ram(vm_size))
            root_disk_type = self._get_root_disk_type(vm_size)
            instance_type = self._get_instance_type(vm_size)
            billing_unit = self._get_billing_unit(vm_size)
            platform = self._get_platform(vm_size)
            country = self._get_country()
            prefix = self._get_prefix()
            extra_attributes = self._get_extra_attributes(vm_size)

            if not platform and self.cc:
                platform = self.cc.cloudName

            for os in self._get_supported_os(vm_size):
                for root_disk in self._get_root_disk_sizes(vm_size, os):
                    price = None
                    raw_price, currency = self._get_price(
                        vm_size, os, root_disk)
                    if raw_price is not None:
                        price = self.convert_currency(currency,
                                                      self.base_currency,
                                                      raw_price)

                    service_offers.append(
                        self._generate_service_offer(
                            connector_instance_name, cpu, ram, root_disk,
                            root_disk_type, os, price, instance_type,
                            self.base_currency, billing_unit, country,
                            platform, prefix, extra_attributes))
        return service_offers

    def do_work(self):
        ch = ConfigHolder(options={
            'verboseLevel': 0,
            'retry': False,
            KEY_RUN_CATEGORY: ''
        },
                          context={'foo': 'bar'})
        self.cc = self.get_connector_class()(ch)
        self.cc._initialization(self.user_info,
                                **self.get_initialization_extra_kwargs())

        self.base_currency = self.get_option(self.BASE_CURRENCY_KEY)

        verbose = self.get_option('verbose')
        dry_run = self.get_option(self.DRY_RUN_KEY)
        ss_endpoint = self.get_option(self.SS_ENDPOINT_KEY)
        ss_username = self.get_option(self.SS_USERNAME_KEY)
        ss_password = self.get_option(self.SS_PASSWORD_KEY)
        connector_instance_name = self.get_option(self.CONNECTOR_NAME_KEY)

        filter_connector_vm = ' and '.join([
            'connector/href="{0}"'.format(connector_instance_name),
            'resource:type="VM"'
        ])

        self.ssapi = Api(endpoint=ss_endpoint, cookie_file=None, insecure=True)
        if not dry_run:
            self.ssapi.login_internal(ss_username, ss_password)

        self._initialize()

        service_offers = self._generate_service_offers(connector_instance_name)

        if not service_offers:
            raise RuntimeError("No service offer found")

        if not dry_run and service_offers:
            self._add_service_attribute_namespace_if_not_exist('resource')
            self._add_service_attribute_namespace_if_not_exist('price')
            prefix = self._get_prefix()
            if prefix:
                self._add_service_attribute_namespace_if_not_exist(prefix)

        service_offers_ids = set()

        for service_offer in service_offers:
            if dry_run:
                print('\nService offer {0}:\n{1}'.format(
                    service_offer['name'], service_offer))
            else:
                cimi_filter = \
                    ' and '.join([filter_connector_vm,
                                  'resource:class="{0}"'.format(service_offer['resource:class']),
                                  'resource:vcpu={0}'.format(service_offer['resource:vcpu']),
                                  'resource:ram={0}'.format(service_offer['resource:ram']),
                                  'resource:disk={0}'.format(service_offer['resource:disk']),
                                  'resource:operatingSystem="{0}"'.format(service_offer['resource:operatingSystem']),
                                  'resource:country="{0}"'.format(service_offer['resource:country']),
                                  'resource:instanceType="{0}"'.format(service_offer['resource:instanceType'])])

                search_result = self.ssapi.cimi_search('serviceOffers',
                                                       filter=cimi_filter)
                result_list = search_result.resources_list
                result_count = len(result_list)

                if result_count == 0:
                    if verbose:
                        print(
                            '\nAddinging the following service offer {0} to {1}...\n{2}'
                            .format(service_offer['name'], ss_endpoint,
                                    service_offer))

                    response = self.ssapi.cimi_add('serviceOffers',
                                                   service_offer)
                    service_offers_ids.add(response.json['resource-id'])
                elif result_count == 1:
                    if verbose:
                        print(
                            '\nUpdating the following service offer {0} to {1}...\n{2}'
                            .format(service_offer['name'], ss_endpoint,
                                    service_offer))

                    response = self.ssapi.cimi_edit(result_list[0].id,
                                                    service_offer)
                    service_offers_ids.add(response.id)
                else:
                    print(
                        '\n!!! Warning duplicates found of following service offer on {0} !!!\n{1}'
                        .format(ss_endpoint, service_offer['name']))
                    for result in result_list:
                        service_offers_ids.add(result.id)

        if not dry_run:
            response = self.ssapi.cimi_search('serviceOffers',
                                              filter=filter_connector_vm)
            old_service_offers_ids = set(r.id for r in response.resources())
            service_offers_ids_to_delete = old_service_offers_ids - service_offers_ids

            for id in service_offers_ids_to_delete:
                if verbose:
                    offer = self.ssapi.cimi_get(id)
                    print(
                        '\nDeleting the following service offer with id {0}...\n{1}'
                        .format(id, offer.json))

                self.ssapi.cimi_delete(id)

        print('\n\nCongratulation, executon completed.')

    def _set_command_specific_options(self, parser):
        parser.add_option('--' + self.BASE_CURRENCY_KEY,
                          dest=self.BASE_CURRENCY_KEY,
                          help='Currency to use',
                          default='EUR',
                          metavar='CURRENCY')

        parser.add_option('--' + self.COUNTRY_KEY,
                          dest=self.COUNTRY_KEY,
                          help='Country where the Cloud reside',
                          default='Unknown',
                          metavar='COUNTRY')

        parser.add_option(
            '--' + self.CONNECTOR_NAME_KEY,
            dest=self.CONNECTOR_NAME_KEY,
            help=
            'Connector instance name to be used as a connector href for service offers',
            default=None,
            metavar='CONNECTOR_NAME')

        parser.add_option(
            '--' + self.SS_ENDPOINT_KEY,
            dest=self.SS_ENDPOINT_KEY,
            help=
            'SlipStream endpoint used where the service offers are pushed to. '
            + '(default: https://nuv.la)',
            default='https://nuv.la',
            metavar='URL')

        parser.add_option('--' + self.SS_USERNAME_KEY,
                          dest=self.SS_USERNAME_KEY,
                          help='Username to be used on SlipStream Endpoint',
                          default=None,
                          metavar='USERNAME')

        parser.add_option('--' + self.SS_PASSWORD_KEY,
                          dest=self.SS_PASSWORD_KEY,
                          help='Password to be used on SlipStream Endpoint',
                          default=None,
                          metavar='PASSWORD')

        parser.add_option('--' + self.DRY_RUN_KEY,
                          dest=self.DRY_RUN_KEY,
                          help='Just print service offers to stdout and exit',
                          action='store_true')

    def _get_command_mandatory_options(self):
        return [self.CONNECTOR_NAME_KEY]
コード例 #3
0
class MainProgram(CommandBase):
    """A command-line program to migrate reports for SS v3.45->3.46."""

    def __init__(self, argv=None):
        self.api = Api('https://localhost', insecure=True)
        self.conn = jaydebeapi.connect("org.hsqldb.jdbcDriver", "jdbc:hsqldb:hsql://localhost/slipstream", ["SA", ""],
                                       "/opt/slipstream/cimi/lib/hsqldb-2.3.4.jar")
        super(MainProgram, self).__init__(argv)

    def parse(self):
        usage = '''usage: %prog [options].
                   This script should be run directly on slipstream server host.
                   Please authenticate with ss-login before executing this script with endpoint=https://localhost'.
                '''

        self.parser.usage = usage
        self.parser.add_option('--months', dest='months',
                               help='Number of months in past from modification time of a report. (default 12 months)',
                               default=12, type=int)

        self.options, self.args = self.parser.parse_args()

    @staticmethod
    def get_all_existing_reports():
        return [y for x in os.walk('/var/tmp/slipstream/reports') for y in glob(os.path.join(x[0], '*.tgz'))]

    @staticmethod
    def filter_reports_updated_since_less_than(reports, months):
        now = int(time.time())
        months_to_seconds = int(months * 2628002.88)
        after_time = now - months_to_seconds
        return [report for report in reports if os.path.getmtime(report) > after_time]

    def create_external_object_report(self, report):
        report_path_split = str.split(report, '/')
        uuid = report_path_split[5]
        report_name = report_path_split[6]
        node_name = str.split(report_name, '_')[0]
        self.db = self.conn.cursor()
        self.db.execute("select USER_ from RUN where UUID='{}'".format(uuid))
        db_res = self.db.fetchone()
        if db_res is None:
            raise Exception('Warning: owner not found for following report: {}'.format(uuid))
        else:
            owner = db_res[0]
        report_object = {'externalObjectTemplate': {'href': 'external-object-template/report',
                                                    'runUUID': uuid,
                                                    'component': node_name,
                                                    'name': report_name,
                                                    'acl': {
                                                        'owner': {
                                                            'principal': owner,
                                                            'type': 'USER'
                                                        },
                                                        'rules': [{
                                                            'principal': owner,
                                                            'right': 'MODIFY',
                                                            'type': 'USER'
                                                        }, {
                                                            'principal': 'ADMIN',
                                                            'right': 'ALL',
                                                            'type': 'ROLE'
                                                        }]
                                                    }}}
        resp = self.api.cimi_add('externalObjects', report_object)
        return resp.json['resource-id']

    def generate_upload_url_external_object_report(self, resource_id):
        resp = self.api.cimi_operation(resource_id, "http://sixsq.com/slipstream/1/action/upload")
        return resp.json['uri']

    def upload_report(self, url, report):
        report_file_data = open(report, 'rb').read()
        response = put(url, data=report_file_data)
        response.raise_for_status()

    def migrate_report(self, report):
        print('Migrating {}'.format(report))
        resource_id = self.create_external_object_report(report)
        upload_url = self.generate_upload_url_external_object_report(resource_id)
        self.upload_report(upload_url, report)

    def doWork(self):
        print(SEPARATOR + 'Starting migration of reports...' + SEPARATOR)
        all_reports = self.get_all_existing_reports()
        print('All reports count: {}'.format(len(all_reports)))
        reports_to_migrate = self.filter_reports_updated_since_less_than(all_reports, self.options.months)
        print('Number of reports updated in last {} months: {}'.format(self.options.months, len(reports_to_migrate))
              + SEPARATOR)

        success = 0
        for report in reports_to_migrate:
            try:
                self.migrate_report(report)
                success += 1
            except Exception as e:
                print("Failed to migrate this report: {} with error => {}".format(report, e.message))
        print(SEPARATOR +
              "Migration completed, {}/{} reports successfully migrated!".format(success, len(reports_to_migrate)))
        exit(0)