def _get_bandwidth_key(items, hourly=True, no_public=False, location=None): """Picks a valid Bandwidth Item, returns the KeyName""" keyName = None # Prefer pay-for-use data transfer with hourly for item in items: capacity = float(item.get('capacity', 0)) # Hourly and private only do pay-as-you-go bandwidth if any([ utils.lookup(item, 'itemCategory', 'categoryCode') != 'bandwidth', (hourly or no_public) and capacity != 0.0, not (hourly or no_public) and capacity == 0.0 ]): continue keyName = item['keyName'] for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue return keyName raise SoftLayerError("Could not find valid price for bandwidth option")
def _get_location(package, location): """Get the longer key with a short location name.""" for region in package['regions']: if region['location']['location']['name'] == location: return region raise SoftLayerError("Could not find valid location for: '%s'" % location)
def _get_item(self, package, flavor): """Returns the item for ordering a dedicated host.""" for item in package['items']: if item['keyName'] == flavor: return item raise SoftLayerError("Could not find valid item for: '%s'" % flavor)
def _get_preset_id(package, size): """Get the preset id given the keyName of the preset.""" for preset in package['activePresets'] + package[ 'accountRestrictedActivePresets']: if preset['keyName'] == size or preset['id'] == size: return preset['id'] raise SoftLayerError("Could not find valid size for: '%s'" % size)
def _get_price(self, package): """Returns valid price for ordering a dedicated host.""" for price in package['prices']: if not price.get('locationGroupId'): return price['id'] raise SoftLayerError("Could not find valid price")
def _get_location(self, regions, datacenter): """Get the longer key with a short location(datacenter) name.""" for region in regions: # list of locations if region['location']['location']['name'] == datacenter: return region raise SoftLayerError("Could not find valid location for: '%s'" % datacenter)
def _get_default_router(self, routers, router_name=None): """Returns the default router for ordering a dedicated host.""" if router_name is None: for router in routers: if router['id'] is not None: return router['id'] else: for router in routers: if router['hostname'] == router_name: return router['id'] raise SoftLayerError("Could not find valid default router")
def _get_id_from_username(self, username): """Looks up a username's id :param string username: Username to lookup :returns: The id that matches username. """ _mask = "mask[id,username]" _filter = { 'hubNetworkStorage': { 'username': utils.query_filter(username) } } account = self.list_accounts(_mask, _filter) if len(account) == 1: return [account[0]['id']] elif len(account) > 1: raise SoftLayerError( "Multiple object storage accounts found with the name: {}". format(username)) else: raise SoftLayerError( "Unable to find object storage account id for: {}".format( username))
def create_guest(self, capacity_id, test, guest_object): """Turns an empty Reserve Capacity into a real Virtual Guest :param int capacity_id: ID of the RESERVED_CAPACITY_GROUP to create this guest into :param bool test: True will use verifyOrder, False will use placeOrder :param dictionary guest_object: Below is the minimum info you need to send in guest_object = { 'domain': 'test.com', 'hostname': 'A1538172419', 'os_code': 'UBUNTU_LATEST_64', 'primary_disk': '25', } """ vs_manager = VSManager(self.client) mask = "mask[instances[id, billingItem[id, item[id,keyName]]], backendRouter[id, datacenter[name]]]" capacity = self.get_object(capacity_id, mask=mask) try: capacity_flavor = capacity['instances'][0]['billingItem']['item'][ 'keyName'] flavor = _flavor_string(capacity_flavor, guest_object['primary_disk']) except KeyError as ex: raise SoftLayerError("Unable to find capacity Flavor.") from ex guest_object['flavor'] = flavor guest_object['datacenter'] = capacity['backendRouter']['datacenter'][ 'name'] # Reserved capacity only supports SAN as of 20181008 guest_object['local_disk'] = False # Reserved capacity only supports monthly ordering via Virtual_Guest::generateOrderTemplate # Hourly ordering would require building out the order manually. guest_object['hourly'] = False template = vs_manager.verify_create_instance(**guest_object) template['reservedCapacityId'] = capacity_id if guest_object.get('ipv6'): ipv6_price = self.ordering_manager.get_price_id_list( 'PUBLIC_CLOUD_SERVER', ['1_IPV6_ADDRESS']) template['prices'].append({'id': ipv6_price[0]}) if test: result = self.client.call('Product_Order', 'verifyOrder', template) else: result = self.client.call('Product_Order', 'placeOrder', template) return result
def _get_os_price_id(items, os, location): """Returns the price id matching.""" for item in items: if any([ utils.lookup(item, 'itemCategory', 'categoryCode') != 'os', utils.lookup(item, 'keyName') != os ]): continue for price in item['prices']: if not _matches_location(price, location): continue return price['id'] raise SoftLayerError("Could not find valid price for os: '%s'" % os)
def _get_default_price_id(items, option, hourly, location): """Returns a 'free' price id given an option.""" for item in items: if utils.lookup(item, 'itemCategory', 'categoryCode') != option: continue for price in item['prices']: if all([ float(price.get('hourlyRecurringFee', 0)) == 0.0, float(price.get('recurringFee', 0)) == 0.0, _matches_billing(price, hourly), _matches_location(price, location) ]): return price['id'] raise SoftLayerError("Could not find valid price for '%s' option" % option)
def _get_extra_price_id(items, key_name, hourly, location): """Returns a price id attached to item with the given key_name.""" for item in items: if utils.lookup(item, 'keyName') != key_name: continue for price in item['prices']: if not _matches_billing(price, hourly): continue if not _matches_location(price, location): continue return price['id'] raise SoftLayerError("Could not find valid price for extra option, '%s'" % key_name)
def _get_port_speed_price_id(items, port_speed, no_public, location): """Choose a valid price id for port speed.""" for item in items: if utils.lookup(item, 'itemCategory', 'categoryCode') != 'port_speed': continue # Check for correct capacity and if the item matches private only if any([ int(utils.lookup(item, 'capacity')) != port_speed, _is_private_port_speed_item(item) != no_public, not _is_bonded(item) ]): continue for price in item['prices']: if not _matches_location(price, location): continue return price['id'] raise SoftLayerError("Could not find valid price for port speed: '%s'" % port_speed)
def cancel_hardware(self, hardware_id, reason='unneeded', comment='', immediate=False): """Cancels the specified dedicated server. Example:: # Cancels hardware id 1234 result = mgr.cancel_hardware(hardware_id=1234) :param int hardware_id: The ID of the hardware to be cancelled. :param string reason: The reason code for the cancellation. This should come from :func:`get_cancellation_reasons`. :param string comment: An optional comment to include with the cancellation. :param bool immediate: If set to True, will automatically update the cancelation ticket to request the resource be reclaimed asap. This request still has to be reviewed by a human :returns: True on success or an exception """ # Get cancel reason reasons = self.get_cancellation_reasons() cancel_reason = reasons.get(reason, reasons['unneeded']) ticket_mgr = TicketManager(self.client) mask = 'mask[id, hourlyBillingFlag, billingItem[id], openCancellationTicket[id], activeTransaction]' hw_billing = self.get_hardware(hardware_id, mask=mask) if 'activeTransaction' in hw_billing: raise SoftLayerError( "Unable to cancel hardware with running transaction") if 'billingItem' not in hw_billing: if utils.lookup(hw_billing, 'openCancellationTicket', 'id'): raise SoftLayerError( "Ticket #%s already exists for this server" % hw_billing['openCancellationTicket']['id']) raise SoftLayerError( "Cannot locate billing for the server. The server may already be cancelled." ) billing_id = hw_billing['billingItem']['id'] if immediate and not hw_billing['hourlyBillingFlag']: LOGGER.warning( "Immediate cancellation of monthly servers is not guaranteed." "Please check the cancellation ticket for updates.") result = self.client.call('Billing_Item', 'cancelItem', False, False, cancel_reason, comment, id=billing_id) hw_billing = self.get_hardware(hardware_id, mask=mask) ticket_number = hw_billing['openCancellationTicket']['id'] cancel_message = "Please reclaim this server ASAP, it is no longer needed. Thankyou." ticket_mgr.update_ticket(ticket_number, cancel_message) LOGGER.info( "Cancelation ticket #%s has been updated requesting immediate reclaim", ticket_number) else: result = self.client.call('Billing_Item', 'cancelItem', immediate, False, cancel_reason, comment, id=billing_id) hw_billing = self.get_hardware(hardware_id, mask=mask) ticket_number = hw_billing['openCancellationTicket']['id'] LOGGER.info("Cancelation ticket #%s has been created", ticket_number) return result
def get_create_options(self, datacenter=None): """Returns valid options for ordering hardware. :param string datacenter: short name, like dal09 """ package = self._get_package() location_group_id = None if datacenter: _filter = {"name": {"operation": datacenter}} _mask = "mask[priceGroups]" dc_details = self.client.call('SoftLayer_Location', 'getDatacenters', mask=_mask, filter=_filter, limit=1) if not dc_details: raise SoftLayerError( "Unable to find a datacenter named {}".format(datacenter)) # A DC will have several price groups, no good way to deal with this other than checking each. # An item should only belong to one type of price group. for group in dc_details[0].get('priceGroups', []): # We only care about SOME of the priceGroups, which all SHOULD start with `Location Group X` # Hopefully this never changes.... if group.get('description').startswith('Location'): location_group_id = group.get('id') # Locations locations = [] for region in package['regions']: if datacenter is None or datacenter == region['location'][ 'location']['name']: locations.append({ 'name': region['location']['location']['longName'], 'key': region['location']['location']['name'], }) # Sizes sizes = [] for preset in package['activePresets'] + package[ 'accountRestrictedActivePresets']: sizes.append({ 'name': preset['description'], 'key': preset['keyName'], 'hourlyRecurringFee': _get_preset_cost(preset, package['items'], 'hourly', location_group_id), 'recurringFee': _get_preset_cost(preset, package['items'], 'monthly', location_group_id) }) operating_systems = [] port_speeds = [] extras = [] for item in package['items']: category = item['itemCategory']['categoryCode'] # Operating systems if category == 'os': operating_systems.append({ 'name': item['softwareDescription']['longDescription'], 'key': item['keyName'], 'referenceCode': item['softwareDescription']['referenceCode'], 'prices': get_item_price(item['prices'], location_group_id) }) # Port speeds elif category == 'port_speed': port_speeds.append({ 'name': item['description'], 'speed': item['capacity'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) # Extras elif category in EXTRA_CATEGORIES: extras.append({ 'name': item['description'], 'key': item['keyName'], 'prices': get_item_price(item['prices'], location_group_id) }) return { 'locations': locations, 'sizes': sizes, 'operating_systems': operating_systems, 'port_speeds': port_speeds, 'extras': extras, }
def _get_backend_router(self, locations, item): """Returns valid router options for ordering a dedicated host.""" mask = ''' id, hostname ''' cpu_count = item['capacity'] for capacity in item['bundleItems']: for category in capacity['categories']: if category['categoryCode'] == 'dedicated_host_ram': mem_capacity = capacity['capacity'] if category['categoryCode'] == 'dedicated_host_disk': disk_capacity = capacity['capacity'] for hardwareComponent in item['bundleItems']: if hardwareComponent['keyName'].find("GPU") != -1: hardwareComponentType = hardwareComponent[ 'hardwareGenericComponentModel']['hardwareComponentType'] gpuComponents = [{ 'hardwareComponentModel': { 'hardwareGenericComponentModel': { 'id': hardwareComponent['hardwareGenericComponentModel'] ['id'], 'hardwareComponentType': { 'keyName': hardwareComponentType['keyName'] } } } }, { 'hardwareComponentModel': { 'hardwareGenericComponentModel': { 'id': hardwareComponent['hardwareGenericComponentModel'] ['id'], 'hardwareComponentType': { 'keyName': hardwareComponentType['keyName'] } } } }] if locations is not None: for location in locations: if location['locationId'] is not None: loc_id = location['locationId'] host = { 'cpuCount': cpu_count, 'memoryCapacity': mem_capacity, 'diskCapacity': disk_capacity, 'datacenter': { 'id': loc_id } } if item['keyName'].find("GPU") != -1: host['pciDevices'] = gpuComponents routers = self.host.getAvailableRouters(host, mask=mask) return routers raise SoftLayerError("Could not find available routers")