Repository: softlayer/softlayer-python

Issue:
Hardware ordering with v4
There are now 3 different methods for ordering hardware (4, if you count Hardware_Server::createObject). They are enumerated below:

* **Normal Hardware Ordering** - uses packages to define different chassis which have different CPU and memory options. Currently, prices inside the package are used to determine which numerical option (ex. 4 CPU cores) matches which price object. The package type is "BARE_METAL_CPU".
* **Bare metal cloud (bmc)** - A limited set of options that was made to (ideally) be more reliable in terms of stock. Hardware_Server::createObject and Hardware_Server::getCreateObjectOptions were made to support bare metal cloud. However, in order to share as much of the existing hardware ordering code that already existed, those interfaces are not used for the CLI/Manager implementation. There are conditionals for BMC servers for ordering, option lookup and canceling. The package type is "BARE_METAL_CORE".
* **Fast hardware provisioning** - Like bare metal cloud, has a limited set of options. However, this is done in a different way than BMC with the concept of order "presets". The suite of presets has its own package (200) and its own package type (BARE_METAL_CPU_FAST_PROVISION). This is currently **not** implemented in the CLI. Here are some API calls that I've found useful:
```python
client['Product_Package'].getActivePresets(id=200)
client['Product_Package_Preset'].getLowestPresetServerPrice(id=68)  # 68 is one of the presets from the previous call.
```

This issue is to track the discussion with the goal of defining what hardware ordering should look like for the version 4 CLI. My primary question is: **Should we support all three ways of hardware ordering in the CLI and in the managers?** Would it be sufficient to only support the fast provisioning servers? Will the fast server provisioning options be implemented with createObject and getCreateObjectOptions? If we're going to support the normal way of ordering hardware, can the manager interface take more of the burden in regard to looking up price ids? Because of the way the interface is, it's impossible to improve upon the implementation without breaking things both in the python and API side.

Discussion:
sudorandom: @underscorephil, @allmightyspiff Let me know your thoughts on this.

Patch:
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 1ab13021..8c2f7a24 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -142,7 +142,6 @@
     ('server:detail', 'SoftLayer.CLI.server.detail:cli'),
     ('server:edit', 'SoftLayer.CLI.server.edit:cli'),
     ('server:list', 'SoftLayer.CLI.server.list:cli'),
-    ('server:list-chassis', 'SoftLayer.CLI.server.list_chassis:cli'),
     ('server:nic-edit', 'SoftLayer.CLI.server.nic_edit:cli'),
     ('server:power-cycle', 'SoftLayer.CLI.server.power:power_cycle'),
     ('server:power-off', 'SoftLayer.CLI.server.power:power_off'),
diff --git a/SoftLayer/CLI/server/__init__.py b/SoftLayer/CLI/server/__init__.py
index 0482f419..72e2e2ff 100644
--- a/SoftLayer/CLI/server/__init__.py
+++ b/SoftLayer/CLI/server/__init__.py
@@ -1,209 +1,1 @@
 """Hardware servers."""
-import re
-
-
-def get_create_options(ds_options, section, pretty=True):
-    """Parse bare metal instance creation options.
-
-    This method can be used to parse the bare metal instance creation
-    options into different sections. This can be useful for data validation
-    as well as printing the options on a help screen.
-
-    :param dict ds_options: The instance options to parse. Must come from
-                             the .get_bare_metal_create_options() function
-                             in the HardwareManager.
-    :param string section: The section to parse out.
-    :param bool pretty: If true, it will return the results in a 'pretty'
-                        format that's easier to print.
-    """
-    return_value = None
-
-    if 'datacenter' == section:
-        datacenters = [loc['keyname']
-                       for loc in ds_options['locations']]
-        return_value = [('datacenter', datacenters)]
-    elif 'cpu' == section and 'server' in ds_options['categories']:
-        results = []
-
-        for item in ds_options['categories']['server']['items']:
-            results.append((
-                item['description'],
-                item['price_id']
-            ))
-
-        return_value = results
-    elif 'memory' == section and 'ram' in ds_options['categories']:
-        ram = []
-        for option in ds_options['categories']['ram']['items']:
-            ram.append((int(option['capacity']), option['price_id']))
-
-        return_value = [('memory', ram)]
-    elif ('server_core' == section
-          and 'server_core' in ds_options['categories']):
-        mem_options = {}
-        cpu_regex = re.compile(r'(\d+) x ')
-        memory_regex = re.compile(r' - (\d+) GB Ram', re.I)
-
-        for item in ds_options['categories']['server_core']['items']:
-            cpu = cpu_regex.search(item['description']).group(1)
-            memory = memory_regex.search(item['description']).group(1)
-
-            if cpu and memory:
-                if memory not in mem_options:
-                    mem_options[memory] = []
-
-                mem_options[memory].append((cpu, item['price_id']))
-
-        results = []
-        for memory in sorted(mem_options.keys(), key=int):
-            key = memory
-
-            if pretty:
-                key = memory
-
-            results.append((key, mem_options[memory]))
-
-        return_value = results
-    elif 'os' == section:
-        os_regex = re.compile(r'(^[A-Za-z\s\/\-]+) ([\d\.]+)')
-        bit_regex = re.compile(r' \((\d+)\s*bit')
-        extra_regex = re.compile(r' - (.+)\(')
-
-        os_list = {}
-        flat_list = []
-
-        # Loop through the operating systems and get their OS codes
-        for opsys in ds_options['categories']['os']['items']:
-            if 'Windows Server' in opsys['description']:
-                os_code = _generate_windows_code(opsys['description'])
-            else:
-                os_results = os_regex.search(opsys['description'])
-
-                # Skip this operating system if it's not parsable
-                if os_results is None:
-                    continue
-
-                name = os_results.group(1)
-                version = os_results.group(2)
-                bits = bit_regex.search(opsys['description'])
-                extra_info = extra_regex.search(opsys['description'])
-
-                if bits:
-                    bits = bits.group(1)
-                if extra_info:
-                    extra_info = extra_info.group(1)
-
-                os_code = _generate_os_code(name, version, bits, extra_info)
-
-            name = os_code.split('_')[0]
-
-            if name not in os_list:
-                os_list[name] = []
-
-            os_list[name].append((os_code, opsys['price_id']))
-            flat_list.append((os_code, opsys['price_id']))
-
-        if pretty:
-            results = []
-            for opsys in sorted(os_list.keys()):
-                results.append(('os (%s)' % opsys, os_list[opsys]))
-
-            return_value = results
-        else:
-            return_value = [('os', flat_list)]
-
-    elif 'disk' == section:
-        disks = []
-        type_regex = re.compile(r'^[\d\.\s]+[GT]B\s+(.+)$')
-        for disk in ds_options['categories']['disk0']['items']:
-            disk_type = 'SATA'
-            if type_regex.match(disk['description']) is not None:
-                disk_type = type_regex.match(disk['description']).group(1)
-            disk_type = disk_type.replace('RPM', '').strip()
-            disk_type = disk_type.replace(' ', '_').upper()
-            disk_type = str(int(disk['capacity'])) + '_' + disk_type
-            disks.append((disk_type, disk['price_id'], disk['id']))
-
-        return_value = [('disk', disks)]
-    elif 'nic' == section:
-        single = []
-        dual = []
-
-        for item in ds_options['categories']['port_speed']['items']:
-            if 'dual' in item['description'].lower():
-                dual.append((str(int(item['capacity'])) + '_DUAL',
-                             item['price_id']))
-            else:
-                single.append((str(int(item['capacity'])),
-                               item['price_id']))
-
-        return_value = [('single nic', single), ('dual nic', dual)]
-    elif 'disk_controller' == section:
-        options = []
-        for item in ds_options['categories']['disk_controller']['items']:
-            text = item['description'].replace(' ', '')
-
-            if 'Non-RAID' == text:
-                text = 'None'
-
-            options.append((text, item['price_id']))
-
-        return_value = [('disk_controllers', options)]
-
-    return return_value
-
-
-def _generate_os_code(name, version, bits, extra_info):
-    """Encapsulates the code for generating the operating system code."""
-    name = name.replace(' Linux', '')
-    name = name.replace('Enterprise', '')
-    name = name.replace('GNU/Linux', '')
-
-    os_code = name.strip().replace(' ', '_').upper()
-
-    if os_code.startswith('RED_HAT'):
-        os_code = 'REDHAT'
-
-    if 'UBUNTU' in os_code:
-        version = re.sub(r'\.\d+', '', version)
-
-    os_code += '_' + version.replace('.0', '')
-
-    if bits:
-        os_code += '_' + bits
-
-    if extra_info:
-        garbage = ['Install', '(32 bit)', '(64 bit)']
-
-        for obj in garbage:
-            extra_info = extra_info.replace(obj, '')
-
-        os_code += '_' + extra_info.strip().replace(' ', '_').upper()
-
-    return os_code
-
-
-def _generate_windows_code(description):
-    """Generates OS codes for windows."""
-    version_check = re.search(r'Windows Server (\d+)', description)
-    version = version_check.group(1)
-
-    os_code = 'WIN_' + version
-
-    if 'Datacenter' in description:
-        os_code += '-DC'
-    elif 'Enterprise' in description:
-        os_code += '-ENT'
-    else:
-        os_code += '-STD'
-
-    if 'ith R2' in description:
-        os_code += '-R2'
-    elif 'ith Hyper-V' in description:
-        os_code += '-HYPERV'
-
-    bit_check = re.search(r'\((\d+)\s*bit', description)
-    if bit_check:
-        os_code += '_' + bit_check.group(1)
-
-    return os_code
diff --git a/SoftLayer/CLI/server/create.py b/SoftLayer/CLI/server/create.py
index 156f8cf2..f1f67de3 100644
--- a/SoftLayer/CLI/server/create.py
+++ b/SoftLayer/CLI/server/create.py
@@ -6,63 +6,25 @@
 from SoftLayer.CLI import exceptions
 from SoftLayer.CLI import formatting
 from SoftLayer.CLI import helpers
-from SoftLayer.CLI import server
 from SoftLayer.CLI import template
 
 import click
 
 
-@click.command(epilog="""See 'sl server list-chassis' and
- 'sl server create-options' for valid options.""")
-@click.option('--domain', '-D',
-              help="Domain portion of the FQDN")
-@click.option('--hostname', '-H',
-              help="Host portion of the FQDN")
-@click.option('--chassis',
-              required=True,
-              help="The chassis to use for the new server")
-@click.option('--cpu', '-c',
-              help="Number of CPU cores",
-              type=click.INT)
-@click.option('--memory', '-m',
-              help="Memory in mebibytes",
-              type=click.INT)
-@click.option('--os', '-o',
-              help="OS install code. Tip: you can specify <OS>_LATEST")
+@click.command(epilog="""See 'sl server create-options' for valid options.""")
+@click.option('--domain', '-D', help="Domain portion of the FQDN")
+@click.option('--hostname', '-H', help="Host portion of the FQDN")
+@click.option('--size', '-s', help="Hardware size")
+@click.option('--os', '-o', help="OS install code")
+@click.option('--datacenter', '-d', help="Datacenter shortname")
+@click.option('--port-speed', type=click.INT, help="Port speeds")
 @click.option('--billing',
               type=click.Choice(['hourly', 'monthly']),
-              default='monthly',
-              help="""Billing rate.
-The hourly rate is only available on bare metal instances""")
-@click.option('--datacenter', '-d', help="Datacenter shortname")
-@click.option('--dedicated/--public',
-              is_flag=True,
-              help="Create a dedicated Virtual Server (Private Node)")
-@click.option('--san',
-              is_flag=True,
-              help="Use SAN storage instead of local disk.")
-@click.option('--test',
-              is_flag=True,
-              help="Do not actually create the virtual server")
-@click.option('--export',
-              type=click.Path(writable=True, resolve_path=True),
-              help="Exports options to a template file")
+              default='hourly',
+              help="Billing rate")
 @click.option('--postinstall', '-i', help="Post-install script to download")
 @helpers.multi_option('--key', '-k',
                       help="SSH keys to add to the root user")
-@helpers.multi_option('--disk', help="Disk sizes")
-@click.option('--controller', help="The RAID configuration for the server")
-@click.option('--private',
-              is_flag=True,
-              help="Forces the VS to only have access the private network")
-@click.option('--network', '-n', help="Network port speed in Mbps")
-@click.option('--template', '-t',
-              help="A template file that defaults the command-line options",
-              type=click.Path(exists=True, readable=True, resolve_path=True))
-@click.option('--userdata', '-u', help="User defined metadata string")
-@click.option('--userfile', '-F',
-              help="Read userdata from file",
-              type=click.Path(exists=True, readable=True, resolve_path=True))
 @click.option('--vlan-public',
               help="The ID of the public VLAN on which you want the virtual "
               "server placed",
@@ -71,6 +33,16 @@
               help="The ID of the private VLAN on which you want the virtual "
                    "server placed",
               type=click.INT)
+@helpers.multi_option('--extra', '-e', help="Extra options")
+@click.option('--test',
+              is_flag=True,
+              help="Do not actually create the virtual server")
+@click.option('--template', '-t',
+              help="A template file that defaults the command-line options",
+              type=click.Path(exists=True, readable=True, resolve_path=True))
+@click.option('--export',
+              type=click.Path(writable=True, resolve_path=True),
+              help="Exports options to a template file")
 @click.option('--wait',
               type=click.INT,
               help="Wait until the server is finished provisioning for up to "
@@ -79,12 +51,31 @@
 def cli(env, **args):
     """Order/create a dedicated server."""
 
-    template.update_with_template_args(args, list_args=['disk', 'key'])
+    template.update_with_template_args(args, list_args=['key'])
     mgr = SoftLayer.HardwareManager(env.client)
 
-    ds_options = mgr.get_dedicated_server_create_options(args['chassis'])
+    # Get the SSH keys
+    ssh_keys = []
+    for key in args.get('key'):
+        resolver = SoftLayer.SshKeyManager(env.client).resolve_ids
+        key_id = helpers.resolve_id(resolver, key, 'SshKey')
+        ssh_keys.append(key_id)
 
-    order = _process_args(env, args, ds_options)
+    order = {
+        'hostname': args['hostname'],
+        'domain': args['domain'],
+        'size': args['size'],
+        'public_vlan': args.get('vlan_public'),
+        'private_vlan': args.get('vlan_private'),
+        'location': args.get('datacenter'),
+        'ssh_keys': ssh_keys,
+        'post_uri': args.get('postinstall'),
+        'os': args['os'],
+        'hourly': args.get('billing') == 'hourly',
+        'port_speed': args.get('port_speed'),
+        'no_public': args.get('no_public') or False,
+        'extras': args.get('extra'),
+    }
 
     # Do not create hardware server with --test or --export
     do_create = not (args['export'] or args['test'])
@@ -134,158 +125,3 @@ def cli(env, **args):
         output = table
 
     return output
-
-
-def _process_args(env, args, ds_options):
-    """Convert CLI arguments to VSManager.create_hardware arguments."""
-    mgr = SoftLayer.HardwareManager(env.client)
-
-    order = {
-        'hostname': args['hostname'],
-        'domain': args['domain'],
-        'bare_metal': False,
-        'package_id': args['chassis'],
-    }
-
-    # Determine if this is a "Bare Metal Instance" or regular server
-    bmc = False
-    if args['chassis'] == str(mgr.get_bare_metal_package_id()):
-        bmc = True
-
-    # Convert the OS code back into a price ID
-    os_price = _get_price_id_from_options(ds_options, 'os', args['os'])
-
-    if os_price:
-        order['os'] = os_price
-    else:
-        raise exceptions.CLIAbort('Invalid operating system specified.')
-
-    order['location'] = args['datacenter']
-
-    if bmc:
-        order['server'] = _get_cpu_and_memory_price_ids(ds_options,
-                                                        args['cpu'],
-                                                        args['memory'])
-        order['bare_metal'] = True
-
-        if args['billing'] == 'hourly':
-            order['hourly'] = True
-    else:
-        order['server'] = args['cpu']
-        order['ram'] = _get_price_id_from_options(
-            ds_options, 'memory', int(args['memory']))
-
-    # Set the disk sizes
-    disk_prices = []
-    disk_number = 0
-    for disk in args.get('disk'):
-        disk_price = _get_disk_price(ds_options, disk, disk_number)
-        disk_number += 1
-        if disk_price:
-            disk_prices.append(disk_price)
-
-    if not disk_prices:
-        disk_prices.append(_get_default_value(ds_options, 'disk0'))
-
-    order['disks'] = disk_prices
-
-    # Set the disk controller price
-    if not bmc:
-        if args.get('controller'):
-            dc_price = _get_price_id_from_options(ds_options,
-                                                  'disk_controller',
-                                                  args.get('controller'))
-        else:
-            dc_price = _get_price_id_from_options(ds_options,
-                                                  'disk_controller',
-                                                  'None')
-
-        order['disk_controller'] = dc_price
-
-    # Set the port speed
-    port_speed = args.get('network')
-
-    nic_price = _get_price_id_from_options(ds_options, 'nic', port_speed)
-
-    if nic_price:
-        order['port_speed'] = nic_price
-    else:
-        raise exceptions.CLIAbort('Invalid NIC speed specified.')
-
-    if args.get('postinstall'):
-        order['post_uri'] = args.get('postinstall')
-
-    # Get the SSH keys
-    if args.get('key'):
-        keys = []
-        for key in args.get('key'):
-            resolver = SoftLayer.SshKeyManager(env.client).resolve_ids
-            key_id = helpers.resolve_id(resolver, key, 'SshKey')
-            keys.append(key_id)
-        order['ssh_keys'] = keys
-
-    if args.get('vlan_public'):
-        order['public_vlan'] = args['vlan_public']
-
-    if args.get('vlan_private'):
-        order['private_vlan'] = args['vlan_private']
-
-    return order
-
-
-def _get_default_value(ds_options, option):
-    """Returns a 'free' price id given an option."""
-    if option not in ds_options['categories']:
-        return
-
-    for item in ds_options['categories'][option]['items']:
-        if not any([
-                float(item.get('setupFee', 0)),
-                float(item.get('recurringFee', 0)),
-                float(item.get('hourlyRecurringFee', 0)),
-                float(item.get('oneTimeFee', 0)),
-                float(item.get('laborFee', 0)),
-        ]):
-            return item['price_id']
-
-
-def _get_disk_price(ds_options, value, number):
-    """Returns a price id that matches a given disk config."""
-    if not number:
-        return _get_price_id_from_options(ds_options, 'disk', value)
-    # This will get the item ID for the matching identifier string, which
-    # we can then use to get the price ID for our specific disk
-    item_id = _get_price_id_from_options(ds_options, 'disk', value, True)
-    key = 'disk' + str(number)
-    if key in ds_options['categories']:
-        for item in ds_options['categories'][key]['items']:
-            if item['id'] == item_id:
-                return item['price_id']
-
-
-def _get_cpu_and_memory_price_ids(ds_options, cpu_value, memory_value):
-    """Returns a price id for a cpu/memory pair in pre-configured servers.
-
-    (formerly known as BMC).
-    """
-    for memory, options in server.get_create_options(ds_options,
-                                                     'server_core',
-                                                     False):
-        if int(memory) == int(memory_value):
-            for cpu_size, price_id in options:
-                if int(cpu_size) == int(cpu_value):
-                    return price_id
-
-    raise exceptions.CLIAbort('No price found for CPU/Memory combination')
-
-
-def _get_price_id_from_options(ds_options, option, value, item_id=False):
-    """Returns a price_id for a given option and value."""
-
-    for _, options in server.get_create_options(ds_options, option, False):
-        for item_options in options:
-            if item_options[0] == value:
-                if not item_id:
-                    return item_options[1]
-                return item_options[2]
-    raise exceptions.CLIAbort('No price found for %s' % option)
diff --git a/SoftLayer/CLI/server/create_options.py b/SoftLayer/CLI/server/create_options.py
index d1d266c5..5e0ce7f1 100644
--- a/SoftLayer/CLI/server/create_options.py
+++ b/SoftLayer/CLI/server/create_options.py
@@ -1,106 +1,55 @@
 """Server order options for a given chassis."""
 # :license: MIT, see LICENSE for more details.
-import os
-
-import SoftLayer
 from SoftLayer.CLI import environment
-from SoftLayer.CLI import exceptions
 from SoftLayer.CLI import formatting
-from SoftLayer.CLI import server
+from SoftLayer.managers import hardware
 
 import click
 
 
 @click.command()
-@click.argument('chassis-id')
 @environment.pass_env
-def cli(env, chassis_id):
+def cli(env):
     """Server order options for a given chassis."""
 
-    mgr = SoftLayer.HardwareManager(env.client)
-
-    table = formatting.KeyValueTable(['Name', 'Value'])
-    table.align['Name'] = 'r'
-    table.align['Value'] = 'l'
-
-    found = False
-    for chassis in mgr.get_available_dedicated_server_packages():
-        if chassis_id == str(chassis[0]):
-            found = True
-            break
-
-    if not found:
-        raise exceptions.CLIAbort('Invalid chassis specified.')
-
-    ds_options = mgr.get_dedicated_server_create_options(chassis_id)
-
-    # Determine if this is a "Bare Metal Instance" or regular server
-    bmc = False
-    if chassis_id == str(mgr.get_bare_metal_package_id()):
-        bmc = True
-
-    results = server.get_create_options(ds_options, 'datacenter')[0]
-    table.add_row([results[0], formatting.listing(sorted(results[1]))])
-
-    # BMC options
-    if bmc:
-        # CPU and memory options
-        results = server.get_create_options(ds_options, 'server_core')
-        memory_cpu_table = formatting.Table(['memory', 'cpu'])
-        for result in results:
-            memory_cpu_table.add_row([
-                result[0],
-                formatting.listing(
-                    [item[0] for item in sorted(
-                        result[1], key=lambda x: int(x[0])
-                    )])])
-        table.add_row(['memory/cpu', memory_cpu_table])
-
-    # Normal hardware options
-    else:
-        # CPU options
-        results = server.get_create_options(ds_options, 'cpu')
-        cpu_table = formatting.Table(['ID', 'Description'])
-        cpu_table.align['ID'] = 'r'
-        cpu_table.align['Description'] = 'l'
-
-        for result in sorted(results, key=lambda x: x[1]):
-            cpu_table.add_row([result[1], result[0]])
-        table.add_row(['cpu', cpu_table])
-
-        # Memory options
-        results = server.get_create_options(ds_options, 'memory')[0]
-        table.add_row([results[0], formatting.listing(
-            item[0] for item in sorted(results[1]))])
-
-        # Disk controller options
-        results = server.get_create_options(ds_options, 'disk_controller')[0]
-        table.add_row([results[0], formatting.listing(
-            item[0] for item in sorted(results[1],))])
-
-    # Disk options
-    results = server.get_create_options(ds_options, 'disk')[0]
-    table.add_row([
-        results[0],
-        formatting.listing(
-            [item[0] for item in sorted(results[1])],
-            separator=os.linesep
-        )])
-
-    # Operating system options
-    results = server.get_create_options(ds_options, 'os')
-    for result in results:
-        table.add_row([
-            result[0],
-            formatting.listing(
-                [item[0] for item in sorted(result[1])],
-                separator=os.linesep
-            )])
-
-    # NIC options
-    results = server.get_create_options(ds_options, 'nic')
-    for result in results:
-        table.add_row([result[0], formatting.listing(
-            item[0] for item in sorted(result[1],))])
-
-    return table
+    hardware_manager = hardware.HardwareManager(env.client)
+    options = hardware_manager.get_create_options()
+
+    tables = []
+
+    # Datacenters
+    dc_table = formatting.Table(['datacenter', 'value'])
+    dc_table.sortby = 'value'
+    for location in options['locations']:
+        dc_table.add_row([location['name'], location['key']])
+    tables.append(dc_table)
+
+    # Presets
+    preset_table = formatting.Table(['size', 'value'])
+    preset_table.sortby = 'value'
+    for size in options['sizes']:
+        preset_table.add_row([size['name'], size['key']])
+    tables.append(preset_table)
+
+    # Operating systems
+    os_table = formatting.Table(['operating_system', 'value'])
+    os_table.sortby = 'value'
+    for operating_system in options['operating_systems']:
+        os_table.add_row([operating_system['name'], operating_system['key']])
+    tables.append(os_table)
+
+    # Port speed
+    port_speed_table = formatting.Table(['port_speed', 'value'])
+    port_speed_table.sortby = 'value'
+    for speed in options['port_speeds']:
+        port_speed_table.add_row([speed['name'], speed['key']])
+    tables.append(port_speed_table)
+
+    # Extras
+    extras_table = formatting.Table(['extras', 'value'])
+    extras_table.sortby = 'value'
+    for extra in options['extras']:
+        extras_table.add_row([extra['name'], extra['key']])
+    tables.append(extras_table)
+
+    return formatting.listing(tables, separator='\n')
diff --git a/SoftLayer/CLI/server/list_chassis.py b/SoftLayer/CLI/server/list_chassis.py
deleted file mode 100644
index 638c9e5e..00000000
--- a/SoftLayer/CLI/server/list_chassis.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""Display a list of available chassis."""
-# :license: MIT, see LICENSE for more details.
-
-import SoftLayer
-from SoftLayer.CLI import environment
-from SoftLayer.CLI import formatting
-
-import click
-
-
-@click.command()
-@environment.pass_env
-def cli(env):
-    """Display a list of available chassis."""
-
-    table = formatting.Table(['code', 'chassis'])
-    table.align['code'] = 'r'
-    table.align['chassis'] = 'l'
-
-    mgr = SoftLayer.HardwareManager(env.client)
-    chassis_list = mgr.get_available_dedicated_server_packages()
-
-    for code, name, _ in chassis_list:
-        table.add_row([code, name])
-
-    return table
diff --git a/SoftLayer/CLI/virt/create.py b/SoftLayer/CLI/virt/create.py
index 369b33b2..0bf4fca1 100644
--- a/SoftLayer/CLI/virt/create.py
+++ b/SoftLayer/CLI/virt/create.py
@@ -25,7 +25,7 @@
 @click.option('--billing',
               type=click.Choice(['hourly', 'monthly']),
               default='hourly',
-              help="""Billing rate""")
+              help="Billing rate")
 @click.option('--datacenter', '-d', help="Datacenter shortname")
 @click.option('--dedicated/--public',
               is_flag=True,
@@ -114,7 +114,7 @@ def cli(env, **args):
             total = total_monthly
 
         billing_rate = 'monthly'
-        if args.get('hourly'):
+        if args.get('billing') == 'hourly':
             billing_rate = 'hourly'
         table.add_row(['Total %s cost' % billing_rate, "%.2f" % total])
         output.append(table)
diff --git a/SoftLayer/managers/hardware.py b/SoftLayer/managers/hardware.py
index 434890e2..1a1e350f 100644
--- a/SoftLayer/managers/hardware.py
+++ b/SoftLayer/managers/hardware.py
@@ -7,11 +7,16 @@
 """
 import socket
 
+import SoftLayer
 from SoftLayer.managers import ordering
 from SoftLayer import utils
 # Invalid names are ignored due to long method names and short argument names
 # pylint: disable=invalid-name, no-self-use
 
+EXTRA_CATEGORIES = ['pri_ipv6_addresses',
+                    'static_ipv6_addresses',
+                    'sec_ip_addresses']
+
 
 class HardwareManager(utils.IdentifierMixin, object):
     """Manage hardware devices.
@@ -169,86 +174,6 @@ def list_hardware(self, tags=None, cpus=None, memory=None, hostname=None,
         kwargs['filter'] = _filter.to_dict()
         return self.account.getHardware(**kwargs)
 
-    def get_bare_metal_create_options(self):
-        """Retrieves the available options for creating a bare metal server.
-
-        :returns: A dictionary of creation options. The categories to order are
-                  contained within the 'categories' key. See
-                  :func:`_parse_package_data` for detailed information.
-
-        .. note::
-
-           The information for ordering bare metal instances comes from
-           multiple API calls. In order to make the process easier, this
-           function will make those calls and reformat the results into a
-           dictionary that's easier to manage. It's recommended that you cache
-           these results with a reasonable lifetime for performance reasons.
-        """
-        hw_id = self.get_bare_metal_package_id()
-
-        if not hw_id:
-            return None
-
-        return self._parse_package_data(hw_id)
-
-    def get_bare_metal_package_id(self):
-        """Return the bare metal package id."""
-        ordering_manager = self.ordering_manager
-        mask = "mask[id,name,description,type[keyName]]"
-        package = ordering_manager.get_package_by_type('BARE_METAL_CORE', mask)
-
-        return package['id']
-
-    def get_available_dedicated_server_packages(self):
-        """Retrieves a list of packages for ordering dedicated servers.
-
-        :returns: A list of tuples of available dedicated server packages in
-                  the form (id, name, description)
-        """
-        available_packages = []
-        ordering_manager = self.ordering_manager
-
-        mask = 'id,name,description,type,isActive'
-        package_types = ['BARE_METAL_CPU',
-                         'BARE_METAL_CORE']
-
-        packages = ordering_manager.get_packages_of_type(package_types,
-                                                         mask)
-        # We only want packages that are active (we can place new orders for)
-        # and non-outlet.
-        # Outlet packages require specialized logic and we don't want to deal
-        # with them right now.
-        packages = ordering_manager.get_only_active_packages(packages)
-        packages = ordering_manager.filter_outlet_packages(packages)
-
-        for package in packages:
-            available_packages.append((package['id'], package['name'],
-                                       package.get('description', None)))
-
-        return available_packages
-
-    def get_dedicated_server_create_options(self, package_id):
-        """Returns chassis-specific options for creating a dedicated server.
-
-        The chassis is based on package ID.
-
-        :param int package_id: The package ID to retrieve the creation options
-                               for. This should come from
-                               :func:`get_available_dedicated_server_packages`.
-        :returns: A dictionary of creation options. The categories to order are
-                  contained within the 'categories' key. See
-                  :func:`_parse_package_data` for detailed information.
-
-        .. note::
-
-           The information for ordering dedicated servers comes from multiple
-           API calls. In order to make the process simpler, this function will
-           make those calls and reformat the results into a dictionary that's
-           easier to manage. It's recommended that you cache these results with
-           a reasonable lifetime for performance reasons.
-        """
-        return self._parse_package_data(package_id)
-
     def get_hardware(self, hardware_id, **kwargs):
         """Get details about a hardware device.
 
@@ -345,104 +270,24 @@ def change_port_speed(self, hardware_id, public, speed):
     def place_order(self, **kwargs):
         """Places an order for a piece of hardware.
 
-        Translates a list of arguments into a dictionary necessary for creating
-        a server.
-
-        .. warning::
-           All items here must be price IDs, NOT quantities!
-
-        :param int server: The identification string for the server to
-                           order. This will either be the CPU/Memory
-                           combination ID for bare metal instances or the
-                           CPU model for dedicated servers.
-        :param string hostname: The hostname to use for the new server.
-        :param string domain: The domain to use for the new server.
-        :param bool hourly: Flag to indicate if this server should be billed
-                            hourly (default) or monthly. Only applies to bare
-                            metal instances.
-        :param string location: The location string (data center) for the
-                                server
-        :param int os: The operating system to use
-        :param array disks: An array of disks for the server. Disks will be
-                            added in the order specified.
-        :param int port_speed: The port speed for the server.
-        :param bool bare_metal: Flag to indicate if this is a bare metal server
-                                or a dedicated server (default).
-        :param int ram: The amount of RAM to order. Only applies to dedicated
-                        servers.
-        :param int package_id: The package_id to use for the server. This
-                               should either be a chassis ID for dedicated
-                               servers or the bare metal instance package ID,
-                               which can be obtained by calling
-                               get_bare_metal_package_id
-        :param int disk_controller: The disk controller to use.
-        :param list ssh_keys: The SSH keys to add to the root user
-        :param int public_vlan: The ID of the public VLAN on which you want
-                                this server placed.
-        :param int private_vlan: The ID of the public VLAN on which you want
-                                 this server placed.
+        See get_create_options() for valid arguments.
+
+        :param string size: server size name
+        :param string hostname: server hostname
+        :param string domain: server domain name
+        :param string location: location (datacenter) name
+        :param string os: operating system name
+        :param int port_speed: Port speed in Mbps
+        :param list ssh_keys: list of ssh key ids
+        :param int public_vlan: public vlan id
+        :param int private_vlan: private vlan id
         :param string post_uri: The URI of the post-install script to run
                                 after reload
-
-        .. warning::
-           Due to how the ordering structure currently works, all ordering
-           takes place using price IDs rather than quantities. See the
-           following sample for an example of using HardwareManager functions
-           for ordering a basic server.
-
-        ::
-
-           # client is assumed to be an initialized SoftLayer.API.Client object
-           mgr = HardwareManager(client)
-
-           # Package ID 32 corresponds to the 'Quad Processor, Quad Core Intel'
-           # package. This information can be obtained from the
-           # :func:`get_available_dedicated_server_packages` function.
-           options = mgr.get_dedicated_server_create_options(32)
-
-           # Review the contents of options to find the information that
-           # applies to your order. For the sake of this example, we assume
-           # that your selections are a series of item IDs for each category
-           # organized into a key-value dictionary.
-
-           # This contains selections for all required categories
-           selections = {
-               'server': 542, # Quad Processor Quad Core Intel 7310 - 1.60GHz
-               'pri_ip_addresses': 15, # 1 IP Address
-               'notification': 51, # Email and Ticket
-               'ram': 280, # 16 GB FB-DIMM Registered 533/667
-               'bandwidth': 173, # 5000 GB Bandwidth
-               'lockbox': 45, # 1 GB Lockbox
-               'monitoring': 49, # Host Ping
-               'disk0': 14, # 500GB SATA II (for the first disk)
-               'response': 52, # Automated Notification
-               'port_speed': 187, # 100 Mbps Public & Private Networks
-               'power_supply': 469, # Redundant Power Supplies
-               'disk_controller': 487, # Non-RAID
-               'vulnerability_scanner': 307, # Nessus
-               'vpn_management': 309, # Unlimited SSL VPN Users
-               'remote_management': 504, # Reboot / KVM over IP
-               'os': 4166, # Ubuntu Linux 12.04 LTS Precise Pangolin (64 bit)
-           }
-
-           args = {
-               'location': 'FIRST_AVAILABLE', # Pick the first available DC
-               'packageId': 32, # From above
-               'disks': [],
-           }
-
-           for cat, item_id in selections:
-               for item in options['categories'][cat]['items'].items():
-                   if item['id'] == item_id:
-                       if 'disk' not in cat or 'disk_controller' == cat:
-                           args[cat] = item['price_id']
-                       else:
-                           args['disks'].append(item['price_id'])
-
-           # You can call :func:`verify_order` here to test the order instead
-           # of actually placing it if you prefer.
-           result = mgr.place_order(**args)
-
+        :param boolean hourly: True if using hourly pricing (default).
+                               False for monthly.
+        :param boolean no_public: True if this server should only have private
+                                  interfaces
+        :param list extras: List of extra feature names
         """
         create_options = self._generate_create_dict(**kwargs)
         return self.client['Product_Order'].placeOrder(create_options)
@@ -474,53 +319,127 @@ def get_cancellation_reasons(self):
             'moving': 'Moving to competitor',
         }
 
-    def _generate_create_dict(
-            self, server=None, hostname=None, domain=None, hourly=False,
-            location=None, os=None, disks=None, port_speed=None,
-            bare_metal=None, ram=None, package_id=None, disk_controller=None,
-            ssh_keys=None, public_vlan=None, private_vlan=None, post_uri=None):
-        """Translates arguments into a dictionary for creating a server.
-
-        .. warning::
-           All items here must be price IDs, NOT quantities!
-
-        :param int server: The identification string for the server to
-                           order. This will either be the CPU/Memory
-                           combination ID for bare metal instances or the
-                           CPU model for dedicated servers.
-        :param string hostname: The hostname to use for the new server.
-        :param string domain: The domain to use for the new server.
-        :param bool hourly: Flag to indicate if this server should be billed
-                            hourly (default) or monthly. Only applies to bare
-                            metal instances.
-        :param string location: The location string (data center) for the
-                                server
-        :param int os: The operating system to use
-        :param array disks: An array of disks for the server. Disks will be
-                            added in the order specified.
-        :param int port_speed: The port speed for the server.
-        :param bool bare_metal: Flag to indicate if this is a bare metal server
-                                or a dedicated server (default).
-        :param int ram: The amount of RAM to order. Only applies to dedicated
-                        servers.
-        :param int package_id: The package_id to use for the server. This
-                               should either be a chassis ID for dedicated
-                               servers or the bare metal instance package ID,
-                               which can be obtained by calling
-                               get_bare_metal_package_id
-        :param int disk_controller: The disk controller to use.
-        :param list ssh_keys: The SSH keys to add to the root user
-        :param int public_vlan: The ID of the public VLAN on which you want
-                                this server placed.
-        :param int private_vlan: The ID of the public VLAN on which you want
-                                 this server placed.
-        """
-        arguments = ['server', 'hostname', 'domain', 'location', 'os', 'disks',
-                     'port_speed', 'bare_metal', 'ram', 'package_id',
-                     'disk_controller', 'server_core', 'disk0']
+    def get_create_options(self):
+        """Returns valid options for ordering hardware."""
+
+        package = self._get_package()
+
+        # Locations
+        locations = []
+        for region in package['regions']:
+            locations.append({
+                'name': region['location']['location']['longName'],
+                'key': region['location']['location']['name'],
+            })
+
+        # Sizes
+        sizes = []
+        for preset in package['activePresets']:
+            sizes.append({
+                'name': preset['description'],
+                'key': preset['keyName']
+            })
+
+        # Operating systems
+        operating_systems = []
+        for item in package['items']:
+            if item['itemCategory']['categoryCode'] == 'os':
+                operating_systems.append({
+                    'name': item['softwareDescription']['longDescription'],
+                    'key': item['softwareDescription']['referenceCode'],
+                })
+
+        # Port speeds
+        port_speeds = []
+        for item in package['items']:
+            if all([item['itemCategory']['categoryCode'] == 'port_speed',
+                    not _is_private_port_speed_item(item)]):
+                port_speeds.append({
+                    'name': item['description'],
+                    'key': item['capacity'],
+                })
+
+        # Extras
+        extras = []
+        for item in package['items']:
+            if item['itemCategory']['categoryCode'] in EXTRA_CATEGORIES:
+                extras.append({
+                    'name': item['description'],
+                    'key': item['keyName']
+                })
+
+        return {
+            'locations': locations,
+            'sizes': sizes,
+            'operating_systems': operating_systems,
+            'port_speeds': port_speeds,
+            'extras': extras,
+        }
+
+    def _get_package(self):
+        """Get the package related to simple hardware ordering."""
+        mask = '''
+items[
+    keyName,
+    capacity,
+    description,
+    attributes[id,attributeTypeKeyName],
+    itemCategory[id,categoryCode],
+    softwareDescription[id,referenceCode,longDescription],
+    prices
+],
+activePresets,
+regions[location[location]]
+'''
+
+        package_type = 'BARE_METAL_CPU_FAST_PROVISION'
+        packages = self.ordering_manager.get_packages_of_type([package_type],
+                                                              mask=mask)
+        if len(packages) != 1:
+            raise SoftLayer.SoftLayerError("Ordering package not found")
+
+        return packages[0]
+
+    def _generate_create_dict(self,
+                              size=None,
+                              hostname=None,
+                              domain=None,
+                              location=None,
+                              os=None,
+                              port_speed=None,
+                              ssh_keys=None,
+                              public_vlan=None,
+                              private_vlan=None,
+                              post_uri=None,
+                              hourly=True,
+                              no_public=False,
+                              extras=None):
+        """Translates arguments into a dictionary for creating a server."""
+
+        extras = extras or []
+
+        package = self._get_package()
+
+        prices = []
+        for category in ['pri_ip_addresses',
+                         'vpn_management',
+                         'remote_management']:
+            prices.append(_get_default_price_id(package['items'],
+                                                category,
+                                                hourly))
+
+        prices.append(_get_os_price_id(package['items'], os))
+        prices.append(_get_bandwidth_price_id(package['items'],
+                                              hourly=hourly,
+                                              no_public=no_public))
+        prices.append(_get_port_speed_price_id(package['items'],
+                                               port_speed,
+                                               no_public))
+
+        for extra in extras:
+            prices.append(_get_extra_price_id(package['items'], extra, hourly))
 
         hardware = {
-            'bareMetalInstanceFlag': bare_metal,
             'hostname': hostname,
             'domain': domain,
         }
@@ -534,8 +453,11 @@ def _generate_create_dict(
 
         order = {
             'hardware': [hardware],
-            'location': location,
-            'prices': [],
+            'location': _get_location_key(package, location),
+            'prices': [{'id': price} for price in prices],
+            'packageId': package['id'],
+            'presetId': _get_preset_id(package, size),
+            'useHourlyPricing': hourly,
         }
 
         if post_uri:
@@ -544,49 +466,6 @@ def _generate_create_dict(
         if ssh_keys:
             order['sshKeys'] = [{'sshKeyIds': ssh_keys}]
 
-        if bare_metal:
-            order['packageId'] = self.get_bare_metal_package_id()
-            order['prices'].append({'id': int(server)})
-            p_options = self.get_bare_metal_create_options()
-            if hourly:
-                order['useHourlyPricing'] = True
-        else:
-            order['packageId'] = package_id
-            order['prices'].append({'id': int(server)})
-            p_options = self.get_dedicated_server_create_options(package_id)
-
-        if disks:
-            for disk in disks:
-                order['prices'].append({'id': int(disk)})
-
-        if os:
-            order['prices'].append({'id': int(os)})
-
-        if port_speed:
-            order['prices'].append({'id': int(port_speed)})
-
-        if ram:
-            order['prices'].append({'id': int(ram)})
-
-        if disk_controller:
-            order['prices'].append({'id': int(disk_controller)})
-
-        # Find all remaining required categories so we can auto-default them
-        required_fields = []
-        for category, data in p_options['categories'].items():
-            if data.get('is_required') and category not in arguments:
-                if 'disk' in category:
-                    # This block makes sure that we can default unspecified
-                    # disks if the user hasn't specified enough.
-                    disk_count = int(category.replace('disk', ''))
-                    if len(disks) >= disk_count + 1:
-                        continue
-                required_fields.append(category)
-
-        for category in required_fields:
-            price = get_default_value(p_options, category, hourly=hourly)
-            order['prices'].append({'id': price})
-
         return order
 
     def _get_ids_from_hostname(self, hostname):
@@ -611,108 +490,6 @@ def _get_ids_from_ip(self, ip):
         if results:
             return [result['id'] for result in results]
 
-    def _parse_package_data(self, package_id):
-        """Parses data from the specified package into a consistent dictionary.
-
-        The data returned by the API varies significantly from one package
-        to another, which means that consuming it can make your program more
-        complicated than desired. This function will make all necessary API
-        calls for the specified package ID and build the results into a
-        consistently formatted dictionary like so:
-
-        result = {
-            'locations': [{'delivery_information': <string>,
-                           'keyname': <string>,
-                           'long_name': <string>}],
-            'categories': {
-                'category_code': {
-                    'sort': <int>,
-                    'step': <int>,
-                    'is_required': <bool>,
-                    'name': <string>,
-                    'group': <string>,
-                    'items': [
-                        {
-                            'id': <int>,
-                            'description': <string>,
-                            'sort': <int>,
-                            'price_id': <int>,
-                            'recurring_fee': <float>,
-                            'setup_fee': <float>,
-                            'hourly_recurring_fee': <float>,
-                            'one_time_fee': <float>,
-                            'labor_fee': <float>,
-                            'capacity': <float>,
-                        }
-                    ]
-                }
-            }
-        }
-
-        Your code can rely upon each of those elements always being present.
-        Each list will contain at least one entry as well, though most will
-        contain more than one.
-        """
-        package = self.client['Product_Package']
-
-        results = {
-            'categories': {},
-            'locations': []
-        }
-
-        # First pull the list of available locations. We do it with the
-        # getObject() call so that we get access to the delivery time info.
-        object_data = package.getRegions(id=package_id)
-
-        for loc in object_data:
-            details = loc['location']['locationPackageDetails'][0]
-
-            results['locations'].append({
-                'delivery_information': details.get('deliveryTimeInformation'),
-                'keyname': loc['keyname'],
-                'long_name': loc['description'],
-            })
-
-        mask = 'mask[itemCategory[group]]'
-
-        for config in package.getConfiguration(id=package_id, mask=mask):
-            code = config['itemCategory']['categoryCode']
-            group = utils.NestedDict(config['itemCategory']) or {}
-            category = {
-                'sort': config['sort'],
-                'step': config['orderStepId'],
-                'is_required': config['isRequired'],
-                'name': config['itemCategory']['name'],
-                'group': group['group']['name'],
-                'items': [],
-            }
-
-            results['categories'][code] = category
-
-        # Now pull in the available package item
-        for category in package.getCategories(id=package_id):
-            code = category['categoryCode']
-            items = []
-
-            for group in category['groups']:
-                for price in group['prices']:
-                    items.append({
-                        'id': price['itemId'],
-                        'description': price['item']['description'],
-                        'sort': price['sort'],
-                        'price_id': price['id'],
-                        'recurring_fee': price.get('recurringFee'),
-                        'setup_fee': price.get('setupFee'),
-                        'hourly_recurring_fee':
-                        price.get('hourlyRecurringFee'),
-                        'one_time_fee': price.get('oneTimeFee'),
-                        'labor_fee': price.get('laborFee'),
-                        'capacity': float(price['item'].get('capacity', 0)),
-                    })
-            results['categories'][code]['items'] = items
-
-        return results
-
     def edit(self, hardware_id, userdata=None, hostname=None, domain=None,
              notes=None):
         """Edit hostname, domain name, notes, user data of the hardware.
@@ -769,36 +546,131 @@ def update_firmware(self,
             id=hardware_id)
 
 
-def get_default_value(package_options, category, hourly=False):
-    """Returns the default price ID for the specified category.
+def _get_extra_price_id(items, key_name, hourly):
+    """Returns a price id attached to item with the given key_name."""
 
-    This determination is made by parsing the items in the package_options
-    argument and finding the first item that has zero specified for every fee
-    field.
+    for item in items:
+        if not utils.lookup(item, 'keyName') == key_name:
+            continue
 
-    .. note::
-       If the category has multiple items with no fee, this will return the
-       first it finds and then short circuit. This may not match the default
-       value presented on the SoftLayer ordering portal. Additionally, this
-       method will return None if there are no free items in the category.
+        for price in item['prices']:
+            if _matches_billing(price, hourly):
+                return price['id']
 
-    :returns: Returns the price ID of the first free item it finds or None
-              if there are no free items.
-    """
-    if category not in package_options['categories']:
-        return
+    raise SoftLayer.SoftLayerError(
+        "Could not find valid price for extra option, '%s'" % key_name)
 
-    for item in package_options['categories'][category]['items']:
-        if hourly:
-            if item.get('hourly_recurring_fee') is None:
-                continue
-        else:
-            if item.get('recurring_fee') is None:
-                continue
-
-        if not any([float(item.get('setup_fee') or 0),
-                    float(item.get('recurring_fee') or 0),
-                    float(item.get('hourly_recurring_fee') or 0),
-                    float(item.get('one_time_fee') or 0),
-                    float(item.get('labor_fee') or 0)]):
-            return item['price_id']
+
+def _get_default_price_id(items, option, hourly):
+    """Returns a 'free' price id given an option."""
+
+    for item in items:
+        if not 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)]):
+                return price['id']
+
+    raise SoftLayer.SoftLayerError(
+        "Could not find valid price for '%s' option" % option)
+
+
+def _get_bandwidth_price_id(items, hourly=True, no_public=False):
+    """Choose a valid price id for bandwidth."""
+
+    # 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([not utils.lookup(item,
+                                 'itemCategory',
+                                 'categoryCode') == 'bandwidth',
+                (hourly or no_public) and capacity != 0.0,
+                not (hourly or no_public) and capacity == 0.0]):
+            continue
+
+        for price in item['prices']:
+            if _matches_billing(price, hourly):
+                return price['id']
+
+    raise SoftLayer.SoftLayerError(
+        "Could not find valid price for bandwidth option")
+
+
+def _get_os_price_id(items, os):
+    """Returns the price id matching."""
+
+    for item in items:
+        if any([not utils.lookup(item,
+                                 'itemCategory',
+                                 'categoryCode') == 'os',
+                not utils.lookup(item,
+                                 'softwareDescription',
+                                 'referenceCode') == os]):
+            continue
+
+        for price in item['prices']:
+            return price['id']
+
+    raise SoftLayer.SoftLayerError("Could not find valid price for os: '%s'" %
+                                   os)
+
+
+def _get_port_speed_price_id(items, port_speed, no_public):
+    """Choose a valid price id for port speed."""
+
+    for item in items:
+        if not 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]):
+            continue
+
+        for price in item['prices']:
+            return price['id']
+
+    raise SoftLayer.SoftLayerError(
+        "Could not find valid price for port speed: '%s'" % port_speed)
+
+
+def _matches_billing(price, hourly):
+    """Return if the price object is hourly and/or monthly."""
+    return any([hourly and price.get('hourlyRecurringFee') is not None,
+                not hourly and price.get('recurringFee') is not None])
+
+
+def _is_private_port_speed_item(item):
+    """Determine if the port speed item is private network only."""
+    for attribute in item['attributes']:
+        if attribute['attributeTypeKeyName'] == 'IS_PRIVATE_NETWORK_ONLY':
+            return True
+
+    return False
+
+
+def _get_location_key(package, location):
+    """Get the longer key with a short location name."""
+    for region in package['regions']:
+        if region['location']['location']['name'] == location:
+            return region['keyname']
+
+    raise SoftLayer.SoftLayerError("Could not find valid location for: '%s'"
+                                   % location)
+
+
+def _get_preset_id(package, size):
+    """Get the preset id given the keyName of the preset."""
+    for preset in package['activePresets']:
+        if preset['keyName'] == size:
+            return preset['id']
+
+    raise SoftLayer.SoftLayerError("Could not find valid size for: '%s'"
+                                   % size)


Test patch:
diff --git a/SoftLayer/testing/fixtures/SoftLayer_Product_Package.py b/SoftLayer/testing/fixtures/SoftLayer_Product_Package.py
index f2621766..b60b9eaa 100644
--- a/SoftLayer/testing/fixtures/SoftLayer_Product_Package.py
+++ b/SoftLayer/testing/fixtures/SoftLayer_Product_Package.py
@@ -1,1039 +1,186 @@
-getAllObjects = [
-    {'id': 13, 'name': 'Mock Testing Package', 'description': 'a thing',
-     'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1},
-    {'id': 15, 'name': 'Inactive package', 'description': 'a cool server',
-     'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 0},
-    {'id': 27, 'name': 'An additional testing category',
-     'description': 'Another thing - OUTLET',
-     'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1},
-    {'id': 28, 'name': 'An outlet package',
-     'description': 'Super fun package',
-     'type': {'keyName': 'BARE_METAL_CPU'}, 'isActive': 1},
-    {'id': 46, 'name': 'Virtual Servers',
-     'description': 'Bare Metal Instance',
-     'type': {'keyName': 'VIRTUAL_SERVER_INSTANCE'}, 'isActive': 1},
-    {'id': 50, 'name': 'Bare Metal Instance',
-     'description': 'Bare Metal Instance',
-     'type': {'keyName': 'BARE_METAL_CORE'}, 'isActive': 1},
-]
-getObject = getAllObjects[0]
+HARDWARE_ITEMS = [
+    {'attributes': [],
+     'capacity': '999',
+     'description': 'Unknown',
+     'itemCategory': {'categoryCode': 'unknown', 'id': 325},
+     'keyName': 'UNKNOWN',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 1245172,
+                 'itemId': 935954,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 0}]},
+    {'attributes': [],
+     'capacity': '64',
+     'description': '1 IPv6 Address',
+     'itemCategory': {'categoryCode': 'pri_ipv6_addresses',
+                      'id': 325},
+     'keyName': '1_IPV6_ADDRESS',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 17129,
+                 'itemId': 4097,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 0}]},
+    {'attributes': [],
+     'capacity': '10',
+     'description': '10 Mbps Public & Private Network Uplinks',
+     'itemCategory': {'categoryCode': 'port_speed', 'id': 26},
+     'keyName': '10_MBPS_PUBLIC_PRIVATE_NETWORK_UPLINKS',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 272,
+                 'itemId': 186,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 5}]},
+    {'attributes': [],
+     'capacity': '0',
+     'description': 'Ubuntu Linux 14.04 LTS Trusty Tahr (64 bit)',
+     'itemCategory': {'categoryCode': 'os', 'id': 12},
+     'keyName': 'OS_UBUNTU_14_04_LTS_TRUSTY_TAHR_64_BIT',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 37650,
+                 'itemId': 4702,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 9}],
+     'softwareDescription': {'id': 1362,
+                             'longDescription': 'Ubuntu / 14.04-64',
+                             'referenceCode': 'UBUNTU_14_64'}},
+    {'attributes': [],
+     'capacity': '1',
+     'description': '1 IP Address',
+     'itemCategory': {'categoryCode': 'pri_ip_addresses', 'id': 13},
+     'keyName': '1_IP_ADDRESS',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 21,
+                 'itemId': 15,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 0}]},
+    {'attributes': [{'attributeTypeKeyName': 'RECLAIM_BYPASS',
+                     'id': 1014}],
+     'description': 'Unlimited SSL VPN Users',
+     'itemCategory': {'categoryCode': 'vpn_management', 'id': 31},
+     'keyName': 'SSL_VPN_USERS_1_PPTP_VPN_USER_PER_ACCOUNT',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 420,
+                 'itemId': 309,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 0}]},
+    {'attributes': [],
+     'description': 'Reboot / KVM over IP',
+     'itemCategory': {'categoryCode': 'remote_management',
+                      'id': 46},
+     'keyName': 'REBOOT_KVM_OVER_IP',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 906,
+                 'itemId': 504,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 0}]},
+    {'attributes': [],
+     'capacity': '0',
+     'description': '0 GB Bandwidth',
+     'itemCategory': {'categoryCode': 'bandwidth', 'id': 10},
+     'keyName': 'BANDWIDTH_0_GB',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'id': 22505,
+                 'itemId': 4481,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'recurringFee': '0',
+                 'setupFee': '0',
+                 'sort': 98}]},
+    {'attributes': [],
+     'capacity': '0',
+     'description': '0 GB Bandwidth',
+     'itemCategory': {'categoryCode': 'bandwidth', 'id': 10},
+     'keyName': 'BANDWIDTH_0_GB_2',
+     'prices': [{'accountRestrictions': [],
+                 'currentPriceFlag': '',
+                 'hourlyRecurringFee': '0',
+                 'id': 1800,
+                 'itemId': 439,
+                 'laborFee': '0',
+                 'onSaleFlag': '',
+                 'oneTimeFee': '0',
+                 'quantity': '',
+                 'setupFee': '0',
+                 'sort': 99}]}]
 
-getConfiguration = [
-    {
-        'sort': 1,
-        'orderStepId': 1,
-        'itemCategory': {
-            'categoryCode': 'server_core',
-            'name': 'Bare Metal Instance'
-        },
-        'isRequired': 0,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'os',
-            'name': 'Operating System',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'disk0',
-            'name': 'First Hard Drive',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'port_speed',
-            'name': 'Uplink Port Speeds'
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-] + [
-    {
-        'itemCategory': {
-            'categoryCode': 'random',
-            'name': 'Random Category',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 0,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'disk0',
-            'name': 'First Disk',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'disk1',
-            'name': 'Second Disk',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'os',
-            'name': 'Operating System',
-        },
-        'sort': 0,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'port_speed',
-            'name': 'Uplink Port Speeds',
-        },
-        'sort': 2,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'ram',
-            'name': 'RAM',
-        },
-        'sort': 2,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'disk_controller',
-            'name': 'Disk Controller',
-        },
-        'sort': 2,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-    {
-        'itemCategory': {
-            'categoryCode': 'server',
-            'name': 'Server',
-        },
-        'sort': 2,
-        'orderStepId': 1,
-        'isRequired': 1,
-    },
-]
-getRegions = [{
-    'location': {
-        'locationPackageDetails': [{
-            'deliveryTimeInformation': 'Typically 2-4 hours',
-        }],
-    },
-    'keyname': 'RANDOM_LOCATION',
-    'description': 'Random unit testing location',
-}]
-
-CATEGORY = {
-    'categoryCode': 'random',
-    'name': 'Random Category',
-}
-
-
-def get_server_categories_mock():
-    prices = [{
-        'itemId': 888,
-        'id': 1888,
-        'sort': 0,
-        'setupFee': 0,
-        'recurringFee': 0,
-        'hourlyRecurringFee': 0,
-        'oneTimeFee': 0,
-        'laborFee': 0,
-        'item': {
-            'id': 888,
-            'description': 'Some item',
-            'capacity': 0,
-        }
-    }]
 
-    disk0_prices = [{
-        'itemId': 2000,
-        'id': 12000,
-        'sort': 0,
-        'setupFee': 0,
-        'recurringFee': 0,
-        'hourlyRecurringFee': 0,
-        'oneTimeFee': 0,
-        'laborFee': 0,
-        'item': {
-            'id': 2000,
-            'description': '1TB Drive',
-            'capacity': 1000,
-        }
-    }]
-
-    disk1_prices = [{
-        'itemId': 2000,
-        'id': 12000,
-        'sort': 0,
-        'setupFee': 0,
-        'recurringFee': 0,
-        'hourlyRecurringFee': 0,
-        'oneTimeFee': 0,
-        'laborFee': 0,
-        'item': {
-            'id': 2000,
-            'description': '1TB Drive',
-            'capacity': 1000,
-        }
-    }]
-
-    centos_prices = [
-        {
-            'itemId': 3907,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 0,
-            'item': {
-                'description': 'CentOS 6.0 (64 bit)',
-                'id': 3907
-            },
-            'id': 13942,
-        },
-    ]
-
-    debian_prices = [
-        {
-            'itemId': 3967,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 6,
-            'item': {
-                'description': 'Debian GNU/Linux 6.0 Squeeze/Stable (32 bit)',
-                'id': 3967
-            },
-            'id': 14046,
-        },
-    ]
-
-    ubuntu_prices = [
-        {
-            'itemId': 4170,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 17438,
-            'sort': 9,
-            'item': {
-                'description': 'Ubuntu Linux 12.04 LTS Precise Pangolin - '
-                               'Minimal Install (64 bit)',
-                'id': 4170
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4166,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 9,
-            'item': {
-                'description': 'Ubuntu Linux 12.04 LTS Precise Pangolin '
-                               '(64 bit)',
-                'id': 4166
-            },
-            'id': 17430,
-        },
-    ]
-
-    windows_prices = [
-        {
-            'itemId': 977,
-            'setupFee': '0',
-            'recurringFee': '40',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 15,
-            'item': {
-                'description': 'Windows Server 2008 R2 Standard Edition '
-                               '(64bit)',
-                'id': 977
-            },
-            'id': 1858,
-        },
-        {
-            'itemId': 978,
-            'setupFee': '0',
-            'recurringFee': '100',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 15,
-            'item': {
-                'description': 'Windows Server 2008 R2 Enterprise Edition '
-                               '(64bit)',
-                'id': 978
-            },
-            'id': 1861,
-        },
-        {
-            'itemId': 980,
-            'setupFee': '0',
-            'recurringFee': '150',
-            'hourlyRecurringFee': '.21',
-            'oneTimeFee': '0',
-            'id': 1867,
-            'sort': 15,
-            'item': {
-                'description': 'Windows Server 2008 R2 Datacenter Edition '
-                               'With Hyper-V (64bit)',
-                'id': 980
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 422,
-            'setupFee': '0',
-            'recurringFee': '40',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 18,
-            'item': {
-                'description': 'Windows Server 2003 Standard SP2 with R2 '
-                               '(64 bit)',
-                'id': 422
-            },
-            'id': 692,
-        },
-    ]
-
-    redhat_prices = [
-        {
-            'itemId': 3841,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 10,
-            'item': {
-                'description': 'Red Hat Enterprise Linux - 6 (64 bit)',
-                'id': 3841
-            },
-            'id': 13800,
-        }
-    ]
-
-    ram_prices = [
-        {
-            'itemId': 254,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 0,
-            'item': {
-                'capacity': '4',
-                'description': '4 GB DIMM Registered 533/667',
-                'id': 254
-            },
-            'id': 1023,
-        },
-        {
-            'itemId': 255,
-            'setupFee': '0',
-            'recurringFee': '30',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 0,
-            'item': {
-                'capacity': '6',
-                'description': '6 GB DIMM Registered 533/667',
-                'id': 255
-            },
-            'id': 702,
-        }]
-
-    server_prices = [
-        {
-            'itemId': 300,
-            'setupFee': '0',
-            'recurringFee': '125',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'currentPriceFlag': '',
-            'sort': 2,
-            'item': {
-                'description': 'Dual Quad Core Pancake 200 - 1.60GHz',
-                'id': 300
-            },
-            'id': 723
-        },
-        {
-            'itemId': 303,
-            'setupFee': '0',
-            'recurringFee': '279',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'currentPriceFlag': '',
-            'sort': 2,
-            'item': {
-                'description': 'Dual Quad Core Pancake 200 - 1.80GHz',
-                'id': 303
-            },
-            'id': 724,
-        }
-    ]
-
-    single_nic_prices = [
-        {
-            'itemId': 187,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 273,
-            'sort': 0,
-            'item': {
-                'capacity': '100',
-                'description': '100 Mbps Public & Private Networks',
-                'id': 187
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 188,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.04',
-            'oneTimeFee': '0',
-            'id': 274,
-            'sort': 0,
-            'item': {
-                'capacity': '1000',
-                'description': '1 Gbps Public & Private Networks',
-                'id': 188
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    dual_nic_prices = [
-        {
-            'itemId': 4332,
-            'setupFee': '0',
-            'recurringFee': '10',
-            'hourlyRecurringFee': '.02',
-            'oneTimeFee': '0',
-            'id': 21509,
-            'sort': 5,
-            'item': {
-                'capacity': '10',
-                'description': '10 Mbps Dual Public & Private Networks '
-                               '(up to 20 Mbps)',
-                'id': 4332
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4336,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.03',
-            'oneTimeFee': '0',
-            'id': 21513,
-            'sort': 5,
-            'item': {
-                'capacity': '100',
-                'description': '100 Mbps Dual Public & Private Networks '
-                               '(up to 200 Mbps)',
-                'id': 4336
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    controller_prices = [
-        {
-            'itemId': 487,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 0,
-            'item': {
-                'description': 'Non-RAID',
-                'id': 487
-            },
-            'id': 876,
-        },
-        {
-            'itemId': 488,
-            'setupFee': '0',
-            'recurringFee': '50',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 0,
-            'item': {
-                'description': 'RAID 0',
-                'id': 488
-            },
-            'id': 877,
-        }
-    ]
-
-    return [
-        {
-            'categoryCode': CATEGORY['categoryCode'],
-            'name': CATEGORY['name'],
-            'id': 1000,
-            'groups': [{
-                'sort': 0,
-                'prices': prices,
-                'itemCategoryId': 1000,
-                'packageId': 13,
-            }],
-        },
-        {
-            'categoryCode': 'ram',
-            'id': 3,
-            'name': 'Ram',
-            'groups': [{
-                'sort': 0,
-                'prices': ram_prices,
-                'itemCategoryId': 3,
-                'packageId': 13
-            }],
-        },
-        {
-            'categoryCode': 'server',
-            'id': 1,
-            'name': 'Server',
-            'groups': [{
-                'sort': 2,
-                'prices': server_prices,
-                'itemCategoryId': 1,
-                'packageId': 13
-            }],
-        },
-        {
-            'categoryCode': 'disk0',
-            'name': 'First Disk',
-            'isRequired': 1,
-            'id': 1001,
-            'groups': [{
-                'sort': 0,
-                'prices': disk0_prices,
-                'itemCategoryId': 1001,
-                'packageId': 13,
-            }],
-        },
-        {
-            'categoryCode': 'disk1',
-            'name': 'Second Disk',
-            'isRequired': 1,
-            'id': 1002,
-            'groups': [{
-                'sort': 0,
-                'prices': disk1_prices,
-                'itemCategoryId': 1002,
-                'packageId': 13,
-            }],
-        },
-        {
-            'categoryCode': 'os',
-            'id': 12,
-            'name': 'Operating System',
-            'groups': [
-                {
-                    'sort': 0,
-                    'prices': centos_prices,
-                    'itemCategoryId': 12,
-                    'packageId': 13,
-                    'title': 'CentOS',
-                },
-                {
-                    'sort': 0,
-                    'prices': debian_prices,
-                    'itemCategoryId': 12,
-                    'packageId': 13,
-                    'title': 'Debian',
-                },
-                {
-                    'sort': 0,
-                    'prices': ubuntu_prices,
-                    'itemCategoryId': 12,
-                    'packageId': 13,
-                    'title': 'Ubuntu',
-                },
-                {
-                    'sort': 0,
-                    'prices': windows_prices,
-                    'itemCategoryId': 12,
-                    'packageId': 13,
-                    'title': 'Microsoft',
-                },
-                {
-                    'sort': 10,
-                    'prices': redhat_prices,
-                    'itemCategoryId': 12,
-                    'packageId': 13,
-                    'title': 'Redhat'
-                },
-            ],
-        },
-        {
-            'categoryCode': 'port_speed',
-            'id': 26,
-            'name': 'Uplink Port Speeds',
-            'groups': [
-                {
-                    'sort': 0,
-                    'prices': single_nic_prices,
-                    'itemCategoryId': 26,
-                    'packageId': 13,
-                },
-                {
-                    'sort': 5,
-                    'prices': dual_nic_prices,
-                    'itemCategoryId': 26,
-                    'packageId': 13,
-                },
-            ],
-        },
-        {
-            'categoryCode': 'disk_controller',
-            'id': 11,
-            'groups': [{
-                'sort': 0,
-                'prices': controller_prices,
-                'itemCategoryId': 11,
-                'packageId': 13}],
-            'name': 'Disk Controller'
-        },
-    ]
-
-
-def get_bmc_categories_mock():
-    server_core_prices = [
-        {
-            'itemId': 1013,
-            'setupFee': '0',
-            'recurringFee': '159',
-            'hourlyRecurringFee': '.5',
-            'oneTimeFee': '0',
-            'id': 1921,
-            'sort': 0,
-            'item': {
-                'capacity': '2',
-                'description': '2 x 2.0 GHz Core Bare Metal Instance - '
-                               '2 GB Ram',
-                'id': 1013
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 1014,
-            'setupFee': '0',
-            'recurringFee': '199',
-            'hourlyRecurringFee': '.75',
-            'oneTimeFee': '0',
-            'id': 1922,
-            'sort': 0,
-            'item': {
-                'capacity': '4',
-                'description': '4 x 2.0 GHz Core Bare Metal Instance - '
-                               '4 GB Ram',
-                'id': 1014
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 1015,
-            'setupFee': '0',
-            'recurringFee': '149',
-            'hourlyRecurringFee': '.75',
-            'oneTimeFee': '0',
-            'id': 1923,
-            'sort': 0,
-            'item': {
-                'capacity': '2',
-                'description': '2 x 2.0 GHz Core Bare Metal Instance - '
-                               '4 GB Ram',
-                'id': 1014
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    centos_prices = [
-        {
-            'itemId': 3921,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 13963,
-            'sort': 0,
-            'item': {
-                'description': 'CentOS 6.0 - Minimal Install (64 bit)',
-                'id': 3921
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 3919,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 13961,
-            'sort': 0,
-            'item': {
-                'description': 'CentOS 6.0 - LAMP Install (64 bit)',
-                'id': 3919
-            },
-            'laborFee': '0',
-        },
-    ]
-
-    redhat_prices = [
-        {
-            'itemId': 3838,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 1,
-            'item': {
-                'description': 'Red Hat Enterprise Linux 6 - Minimal Install '
-                               '(64 bit)',
-                'id': 3838
-            },
-            'id': 13798,
-        },
-        {
-            'itemId': 3834,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'laborFee': '0',
-            'oneTimeFee': '0',
-            'sort': 1,
-            'item': {
-                'description': 'Red Hat Enterprise Linux 6 - LAMP Install '
-                               '(64 bit)',
-                'id': 3834
-            },
-            'id': 13795,
-        }
-    ]
-
-    ubuntu_prices = [
-        {
-            'itemId': 4170,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 17438,
-            'sort': 9,
-            'item': {
-                'description': 'Ubuntu Linux 12.04 LTS Precise Pangolin - '
-                               'Minimal Install (64 bit)',
-                'id': 4170
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4168,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 17434,
-            'sort': 9,
-            'item': {
-                'description': 'Ubuntu Linux 12.04 LTS Precise Pangolin - '
-                               'LAMP Install (64 bit)',
-                'id': 4168
-            },
-            'laborFee': '0',
-        },
-    ]
-
-    windows_prices = [
-        {
-            'itemId': 936,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.05',
-            'oneTimeFee': '0',
-            'id': 1752,
-            'sort': 16,
-            'item': {
-                'description': 'Windows Server 2008 Standard Edition SP2 '
-                               '(64bit)',
-                'id': 936
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 938,
-            'setupFee': '0',
-            'recurringFee': '50',
-            'hourlyRecurringFee': '.1',
-            'oneTimeFee': '0',
-            'id': 1761,
-            'sort': 16,
-            'item': {
-                'description': 'Windows Server 2008 Enterprise Edition SP2 '
-                               '(64bit)',
-                'id': 938
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 940,
-            'setupFee': '0',
-            'recurringFee': '75',
-            'hourlyRecurringFee': '.15',
-            'oneTimeFee': '0',
-            'id': 1770,
-            'sort': 16,
-            'item': {
-                'description': 'Windows Server 2008 Datacenter Edition SP2 '
-                               '(64bit)',
-                'id': 940
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4247,
-            'setupFee': '0',
-            'recurringFee': '75',
-            'hourlyRecurringFee': '.15',
-            'oneTimeFee': '0',
-            'id': 21060,
-            'sort': 17,
-            'item': {
-                'description': 'Windows Server 2012 Datacenter Edition With '
-                               'Hyper-V (64bit)',
-                'id': 4247
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4248,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.05',
-            'oneTimeFee': '0',
-            'id': 21074,
-            'sort': 15,
-            'item': {
-                'description': 'Windows Server 2008 Standard SP1 with R2 '
-                               '(64 bit)',
-                'id': 4248
-            },
-            'laborFee': '0',
-        },
-    ]
-
-    disk_prices1 = [
-        {
-            'itemId': 14,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 1267,
-            'sort': 0,
-            'item': {
-                'capacity': '500',
-                'description': '500GB SATA II',
-                'id': 14
-            },
-            'laborFee': '0',
-        },
-    ]
-
-    disk_prices2 = [
-        {
-            'itemId': 2000,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 19,
-            'sort': 99,
-            'item': {
-                'capacity': '250',
-                'description': '250GB SATA II',
-                'id': 2000
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    single_nic_prices = [
-        {
-            'itemId': 187,
-            'setupFee': '0',
-            'recurringFee': '0',
-            'hourlyRecurringFee': '0',
-            'oneTimeFee': '0',
-            'id': 273,
-            'sort': 0,
-            'item': {
-                'capacity': '100',
-                'description': '100 Mbps Public & Private Networks',
-                'id': 187
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 188,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.04',
-            'oneTimeFee': '0',
-            'id': 274,
-            'sort': 0,
-            'item': {
-                'capacity': '1000',
-                'description': '1 Gbps Public & Private Networks',
-                'id': 188
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    dual_nic_prices = [
-        {
-            'itemId': 4332,
-            'setupFee': '0',
-            'recurringFee': '10',
-            'hourlyRecurringFee': '.02',
-            'oneTimeFee': '0',
-            'id': 21509,
-            'sort': 5,
-            'item': {
-                'capacity': '10',
-                'description': '10 Mbps Dual Public & Private Networks '
-                               '(up to 20 Mbps)',
-                'id': 4332
-            },
-            'laborFee': '0',
-        },
-        {
-            'itemId': 4336,
-            'setupFee': '0',
-            'recurringFee': '20',
-            'hourlyRecurringFee': '.03',
-            'oneTimeFee': '0',
-            'id': 21513,
-            'sort': 5,
-            'item': {
-                'capacity': '100',
-                'description': '100 Mbps Dual Public & Private Networks '
-                               '(up to 200 Mbps)',
-                'id': 4336
-            },
-            'laborFee': '0',
-            'quantity': ''
-        },
-        {
-            'itemId': 1284,
-            'setupFee': '0',
-            'recurringFee': '40',
-            'hourlyRecurringFee': '.05',
-            'oneTimeFee': '0',
-            'id': 2314,
-            'sort': 5,
-            'item': {
-                'capacity': '1000',
-                'description': '1 Gbps Dual Public & Private Networks '
-                               '(up to 2 Gbps)',
-                'id': 1284
-            },
-            'laborFee': '0',
-        }
-    ]
-
-    return [
-        {
-            'categoryCode': 'server_core',
-            'id': 110,
-            'groups': [{
-                'sort': 0,
-                'prices': server_core_prices,
-                'itemCategoryId': 110,
-                'packageId': 50
-            }],
-            'name': 'Bare Metal Instance'
-        },
-        {
-            'categoryCode': 'os',
-            'id': 12,
-            'groups': [
-                {'sort': 0, 'prices': centos_prices, 'title': 'CentOS'},
-                {'sort': 0, 'prices': redhat_prices, 'title': 'Redhat'},
-                {'sort': 9, 'prices': ubuntu_prices, 'title': 'Ubuntu'},
-                {'sort': 14, 'prices': windows_prices, 'title': 'Microsoft'}
-            ],
-            'name': 'Operating System'
-        },
-        {
-            'categoryCode': 'disk0',
-            'id': 4,
-            'groups': [
-                {
-                    'sort': 0,
-                    'prices': disk_prices1,
-                    'itemCategoryId': 4,
-                    'packageId': 50
-                },
-                {
-                    'sort': 99,
-                    'prices': disk_prices2,
-                    'itemCategoryId': 4,
-                    'packageId': 50
-                }
-            ],
-            'name': 'First Hard Drive',
-        },
-        {
-            'categoryCode': 'port_speed',
-            'id': 26,
-            'groups': [
-                {
-                    'sort': 0,
-                    'prices': single_nic_prices,
-                    'itemCategoryId': 26,
-                    'packageId': 50
-                },
-                {
-                    'sort': 5,
-                    'prices': dual_nic_prices,
-                    'itemCategoryId': 26,
-                    'packageId': 50
-                }
-            ],
-            'name': 'Uplink Port Speeds',
-        },
-    ]
+getAllObjects = [{
+    'activePresets': [{
+        'description': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID',
+        'id': 64,
+        'isActive': '1',
+        'keyName': 'S1270_8GB_2X1TBSATA_NORAID',
+        'name': 'S1270 8GB 2X1TBSATA NORAID',
+        'packageId': 200
+    }],
+    'description': 'Bare Metal Server',
+    'firstOrderStepId': 1,
+    'id': 200,
+    'isActive': 1,
+    'items': HARDWARE_ITEMS,
+    'name': 'Bare Metal Server',
+    'regions': [{'description': 'WDC01 - Washington, DC - East Coast U.S.',
+                 'keyname': 'WASHINGTON_DC',
+                 'location': {'location': {'id': 37473,
+                                           'longName': 'Washington 1',
+                                           'name': 'wdc01'}},
+                 'sortOrder': 10}],
+    'subDescription': 'Bare Metal Server',
+    'unitSize': 1,
+}]
 
-getCategories = get_server_categories_mock() + get_bmc_categories_mock()
 getItems = [
     {
         'id': 1234,
@@ -1138,6 +285,8 @@ def get_bmc_categories_mock():
         'itemCategory': {'categoryCode': 'global_ipv6'},
         'prices': [{'id': 611}],
     }]
+
+
 getItemPrices = [
     {
         'currentPriceFlag': '',
diff --git a/SoftLayer/tests/CLI/modules/server_tests.py b/SoftLayer/tests/CLI/modules/server_tests.py
index 675fae24..acbab824 100644
--- a/SoftLayer/tests/CLI/modules/server_tests.py
+++ b/SoftLayer/tests/CLI/modules/server_tests.py
@@ -11,7 +11,6 @@
 import mock
 
 from SoftLayer.CLI import exceptions
-from SoftLayer.CLI.server import create
 from SoftLayer import testing
 
 import json
@@ -26,79 +25,6 @@ def test_server_cancel_reasons(self):
         self.assertEqual(result.exit_code, 0)
         self.assertEqual(len(output), 10)
 
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    def test_server_create_options(self, packages):
-
-        packages.return_value = [(999, 'Chassis 999')]
-
-        result = self.run_command(['server', 'create-options', '999'])
-
-        expected = {
-            'cpu': [
-                {'Description': 'Dual Quad Core Pancake 200 - 1.60GHz',
-                 'ID': 723},
-                {'Description': 'Dual Quad Core Pancake 200 - 1.80GHz',
-                 'ID': 724}],
-            'datacenter': ['RANDOM_LOCATION'],
-            'disk': ['250_SATA_II', '500_SATA_II'],
-            'disk_controllers': ['None', 'RAID0'],
-            'dual nic': ['1000_DUAL', '100_DUAL', '10_DUAL'],
-            'memory': [4, 6],
-            'os (CENTOS)': ['CENTOS_6_64_LAMP', 'CENTOS_6_64_MINIMAL'],
-            'os (REDHAT)': ['REDHAT_6_64_LAMP', 'REDHAT_6_64_MINIMAL'],
-            'os (UBUNTU)': ['UBUNTU_12_64_LAMP', 'UBUNTU_12_64_MINIMAL'],
-            'os (WIN)': [
-                'WIN_2008-DC_64',
-                'WIN_2008-ENT_64',
-                'WIN_2008-STD-R2_64',
-                'WIN_2008-STD_64',
-                'WIN_2012-DC-HYPERV_64'],
-            'single nic': ['100', '1000']}
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertEqual(json.loads(result.output), expected)
-
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    def test_server_create_options_with_invalid_chassis(self, packages):
-        packages.return_value = [(998, 'Legacy Chassis')]
-        result = self.run_command(['server', 'create-options', '999'])
-
-        self.assertEqual(result.exit_code, 2)
-        self.assertIsInstance(result.exception, exceptions.CLIAbort)
-
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    @mock.patch('SoftLayer.HardwareManager.get_bare_metal_package_id')
-    def test_server_create_options_for_bmc(self, bmpi, packages):
-        packages.return_value = [(1099, 'Bare Metal Instance')]
-        bmpi.return_value = '1099'
-
-        result = self.run_command(['server', 'create-options', '1099'])
-
-        expected = {
-            'memory/cpu': [
-                {'cpu': ['2'], 'memory': '2'},
-                {'cpu': ['2', '4'], 'memory': '4'},
-            ],
-            'datacenter': ['RANDOM_LOCATION'],
-            'disk': ['250_SATA_II', '500_SATA_II'],
-            'dual nic': ['1000_DUAL', '100_DUAL', '10_DUAL'],
-            'os (CENTOS)': ['CENTOS_6_64_LAMP', 'CENTOS_6_64_MINIMAL'],
-            'os (REDHAT)': ['REDHAT_6_64_LAMP', 'REDHAT_6_64_MINIMAL'],
-            'os (UBUNTU)': ['UBUNTU_12_64_LAMP', 'UBUNTU_12_64_MINIMAL'],
-            'os (WIN)': [
-                'WIN_2008-DC_64',
-                'WIN_2008-ENT_64',
-                'WIN_2008-STD-R2_64',
-                'WIN_2008-STD_64',
-                'WIN_2012-DC-HYPERV_64'],
-            'single nic': ['100', '1000']}
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertEqual(json.loads(result.output), expected)
-
     def test_server_details(self):
         result = self.run_command(['server', 'detail', '1234',
                                    '--passwords', '--price'])
@@ -280,19 +206,6 @@ def test_nic_edit_server(self):
                                 args=(100,),
                                 identifier=12345)
 
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    def test_list_chassis_server(self, packages):
-        packages.return_value = [(1, 'Chassis 1', 'Some chassis'),
-                                 (2, 'Chassis 2', 'Another chassis')]
-        result = self.run_command(['server', 'list-chassis'])
-
-        expected = [{'chassis': 'Chassis 1', 'code': 1},
-                    {'chassis': 'Chassis 2', 'code': 2}]
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertEqual(json.loads(result.output), expected)
-
     @mock.patch('SoftLayer.HardwareManager.verify_order')
     def test_create_server_test_flag(self, verify_mock):
         verify_mock.return_value = {
@@ -309,26 +222,17 @@ def test_create_server_test_flag(self, verify_mock):
                 }
             ]
         }
-        result = self.run_command(['server', 'create',
-                                   '--chassis=999',
+
+        result = self.run_command(['--really', 'server', 'create',
+                                   '--size=S1270_8GB_2X1TBSATA_NORAID',
                                    '--hostname=test',
                                    '--domain=example.com',
                                    '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--network=100',
-                                   '--disk=250_SATA_II',
-                                   '--disk=250_SATA_II',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--memory=4',
-                                   '--controller=RAID0',
-                                   '--test',
-                                   '--key=1234',
-                                   '--key=456',
+                                   '--port-speed=100',
+                                   '--os=UBUNTU_12_64',
                                    '--vlan-public=10234',
                                    '--vlan-private=20468',
-                                   '--postinstall='
-                                   'http://somescript.foo/myscript.sh',
-                                   ],
+                                   '--test'],
                                   fmt='raw')
 
         self.assertEqual(result.exit_code, 0)
@@ -336,78 +240,20 @@ def test_create_server_test_flag(self, verify_mock):
         self.assertIn("Second Item", result.output)
         self.assertIn("Total monthly cost", result.output)
 
-    @mock.patch('SoftLayer.HardwareManager.verify_order')
-    def test_create_server_test_no_disk(self, verify_mock):
-
-        verify_mock.return_value = {
-            'prices': [
-                {
-                    'recurringFee': 0.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'First Item'},
-                },
-                {
-                    'recurringFee': 25.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'Second Item'},
-                }
-            ]
-        }
-        result = self.run_command(['server', 'create',
-                                   '--chassis=999',
-                                   '--hostname=test',
-                                   '--domain=example.com',
-                                   '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--network=100',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--memory=4',
-                                   '--controller=RAID0',
-                                   '--test',
-                                   '--key=1234',
-                                   '--key=456',
-                                   '--vlan-public=10234',
-                                   '--vlan-private=20468',
-                                   '--postinstall='
-                                   'http://somescript.foo/myscript.sh',
-                                   ],
-                                  fmt='raw')
-
-        self.assertEqual(result.exit_code, 0)
-
-    @mock.patch('SoftLayer.HardwareManager.verify_order')
-    def test_create_server_test_no_disk_no_raid(self, verify_mock):
-        verify_mock.return_value = {
-            'prices': [
-                {
-                    'recurringFee': 0.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'First Item'},
-                },
-                {
-                    'recurringFee': 25.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'Second Item'},
-                }
-            ]
-        }
-
-        result = self.run_command(['server', 'create',
-                                   '--chassis=999',
-                                   '--hostname=test',
-                                   '--domain=example.com',
-                                   '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--network=100',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--memory=4',
-                                   '--test',
-                                   '--vlan-public=10234',
-                                   '--vlan-private=20468',
-                                   ],
-                                  fmt='raw')
+    def test_create_options(self):
+        result = self.run_command(['server', 'create-options'])
 
         self.assertEqual(result.exit_code, 0)
+        expected = [
+            [{'datacenter': 'Washington 1', 'value': 'wdc01'}],
+            [{'size': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID',
+              'value': 'S1270_8GB_2X1TBSATA_NORAID'}],
+            [{'operating_system': 'Ubuntu / 14.04-64',
+              'value': 'UBUNTU_14_64'}],
+            [{'port_speed': '10 Mbps Public & Private Network Uplinks',
+              'value': '10'}],
+            [{'extras': '1 IPv6 Address', 'value': '1_IPV6_ADDRESS'}]]
+        self.assertEqual(json.loads(result.output), expected)
 
     @mock.patch('SoftLayer.HardwareManager.place_order')
     def test_create_server(self, order_mock):
@@ -417,16 +263,15 @@ def test_create_server(self, order_mock):
         }
 
         result = self.run_command(['--really', 'server', 'create',
-                                   '--chassis=999',
+                                   '--size=S1270_8GB_2X1TBSATA_NORAID',
                                    '--hostname=test',
                                    '--domain=example.com',
                                    '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--network=100',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--memory=4',
+                                   '--port-speed=100',
+                                   '--os=UBUNTU_12_64',
                                    '--vlan-public=10234',
                                    '--vlan-private=20468',
+                                   '--key=10',
                                    ])
 
         self.assertEqual(result.exit_code, 0)
@@ -441,141 +286,47 @@ def test_create_server_missing_required(self):
                                    '--hostname=test',
                                    '--domain=example.com',
                                    '--datacenter=TEST00',
-                                   '--cpu=4',
                                    '--network=100',
                                    '--os=UBUNTU_12_64_MINIMAL',
-                                   '--memory=4',
                                    ])
 
         self.assertEqual(result.exit_code, 2)
         self.assertIsInstance(result.exception, SystemExit)
 
     @mock.patch('SoftLayer.CLI.template.export_to_template')
-    def test_create_server_with_export(self, export_to_template):
-        result = self.run_command(['server', 'create',
-                                   # Note: no chassis id
-                                   '--chassis=999',
-                                   '--hostname=test',
-                                   '--domain=example.com',
-                                   '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--memory=4',
-                                   '--network=100',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--key=1234',
-                                   '--export=/path/to/test_file.txt',
-                                   ])
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertIn("Successfully exported options to a template file.",
-                      result.output)
-        export_to_template.assert_called_with('/path/to/test_file.txt',
-                                              {'domain': 'example.com',
-                                               'san': False,
-                                               'dedicated': False,
-                                               'private': False,
-                                               'disk': (),
-                                               'userdata': None,
-                                               'network': '100',
-                                               'billing': 'monthly',
-                                               'userfile': None,
-                                               'hostname': 'test',
-                                               'template': None,
-                                               'memory': 4,
-                                               'test': False,
-                                               'postinstall': None,
-                                               'controller': None,
-                                               'chassis': '999',
-                                               'key': ('1234',),
-                                               'vlan_private': None,
-                                               'wait': None,
-                                               'datacenter': 'TEST00',
-                                               'os': 'UBUNTU_12_64_MINIMAL',
-                                               'cpu': 4,
-                                               'vlan_public': None},
-                                              exclude=['wait', 'test'])
-
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    @mock.patch('SoftLayer.HardwareManager.get_bare_metal_package_id')
-    @mock.patch('SoftLayer.HardwareManager.verify_order')
-    def test_create_server_test_for_bmc(self, verify_mock, bmpi, packages):
-        packages.return_value = [(1099, 'Bare Metal Instance', 'BMC')]
-        bmpi.return_value = '1099'
-
-        # First, test the --test flag
-        verify_mock.return_value = {
-            'prices': [
-                {
-                    'recurringFee': 0.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'First Item'},
-                },
-                {
-                    'recurringFee': 25.0,
-                    'setupFee': 0.0,
-                    'item': {'description': 'Second Item'},
-                }
-            ]
-        }
-
-        result = self.run_command(['server', 'create',
-                                   '--chassis=1099',
-                                   '--hostname=test',
-                                   '--domain=example.com',
-                                   '--datacenter=TEST00',
-                                   '--cpu=2',
-                                   '--memory=2',
-                                   '--network=100',
-                                   '--disk=250_SATA_II',
-                                   '--disk=250_SATA_II',
-                                   '--os=UBUNTU_12_64_MINIMAL',
-                                   '--vlan-public=10234',
-                                   '--vlan-private=20468',
-                                   '--key=1234',
-                                   '--key=456',
-                                   '--test',
-                                   '--postinstall='
-                                   'http://somescript.foo/myscript.sh',
-                                   '--billing=hourly',
-                                   ])
-
-        self.assertEqual(result.exit_code, 0)
-        self.assertIn("First Item", result.output)
-        self.assertIn("Second Item", result.output)
-        self.assertIn("Total monthly cost", result.output)
-
-    @mock.patch('SoftLayer.HardwareManager'
-                '.get_available_dedicated_server_packages')
-    @mock.patch('SoftLayer.HardwareManager.get_bare_metal_package_id')
-    @mock.patch('SoftLayer.HardwareManager.place_order')
-    def test_create_server_for_bmc(self, order_mock, bmpi, packages):
-        order_mock.return_value = {
-            'orderId': 98765,
-            'orderDate': '2013-08-02 15:23:47'
-        }
-
+    def test_create_server_with_export(self, export_mock):
         result = self.run_command(['--really', 'server', 'create',
-                                   '--chassis=1099',
+                                   '--size=S1270_8GB_2X1TBSATA_NORAID',
                                    '--hostname=test',
                                    '--domain=example.com',
                                    '--datacenter=TEST00',
-                                   '--cpu=4',
-                                   '--memory=4',
-                                   '--network=100',
-                                   '--disk=250_SATA_II',
-                                   '--disk=250_SATA_II',
-                                   '--os=UBUNTU_12_64_MINIMAL',
+                                   '--port-speed=100',
+                                   '--os=UBUNTU_12_64',
                                    '--vlan-public=10234',
                                    '--vlan-private=20468',
-                                   '--key=1234',
-                                   '--key=456',
-                                   '--billing=hourly',
-                                   ])
+                                   '--export=/path/to/test_file.txt'],
+                                  fmt='raw')
 
         self.assertEqual(result.exit_code, 0)
-        self.assertEqual(json.loads(result.output),
-                         {'id': 98765, 'created': '2013-08-02 15:23:47'})
+        self.assertIn("Successfully exported options to a template file.",
+                      result.output)
+        export_mock.assert_called_with('/path/to/test_file.txt',
+                                       {'billing': 'hourly',
+                                        'datacenter': 'TEST00',
+                                        'domain': 'example.com',
+                                        'extra': (),
+                                        'hostname': 'test',
+                                        'key': (),
+                                        'os': 'UBUNTU_12_64',
+                                        'port_speed': 100,
+                                        'postinstall': None,
+                                        'size': 'S1270_8GB_2X1TBSATA_NORAID',
+                                        'test': False,
+                                        'vlan_private': 20468,
+                                        'vlan_public': 10234,
+                                        'wait': None,
+                                        'template': None},
+                                       exclude=['wait', 'test'])
 
     def test_edit_server_userdata_and_file(self):
         # Test both userdata and userfile at once
@@ -632,11 +383,6 @@ def test_edit_server_userfile(self):
                                     args=(['some data'],),
                                     identifier=1000)
 
-    def test_get_default_value_returns_none_for_unknown_category(self):
-        option_mock = {'categories': {'cat1': []}}
-        output = create._get_default_value(option_mock, 'nope')
-        self.assertEqual(None, output)
-
     @mock.patch('SoftLayer.CLI.formatting.confirm')
     def test_update_firmware(self, confirm_mock):
         confirm_mock.return_value = True
diff --git a/SoftLayer/tests/managers/hardware_tests.py b/SoftLayer/tests/managers/hardware_tests.py
index 5044e247..283598c9 100644
--- a/SoftLayer/tests/managers/hardware_tests.py
+++ b/SoftLayer/tests/managers/hardware_tests.py
@@ -4,19 +4,37 @@
 
     :license: MIT, see LICENSE for more details.
 """
+import copy
+
 import mock
 
 import SoftLayer
-from SoftLayer.managers import hardware
+from SoftLayer import managers
 from SoftLayer import testing
 from SoftLayer.testing import fixtures
 
 
+MINIMAL_TEST_CREATE_ARGS = {
+    'size': 'S1270_8GB_2X1TBSATA_NORAID',
+    'hostname': 'unicorn',
+    'domain': 'giggles.woo',
+    'location': 'wdc01',
+    'os': 'UBUNTU_14_64',
+    'port_speed': 10,
+}
+
+
 class HardwareTests(testing.TestCase):
 
     def set_up(self):
         self.hardware = SoftLayer.HardwareManager(self.client)
 
+    def test_init_with_ordering_manager(self):
+        ordering_manager = SoftLayer.OrderingManager(self.client)
+        mgr = SoftLayer.HardwareManager(self.client, ordering_manager)
+
+        self.assertEqual(mgr.ordering_manager, ordering_manager)
+
     def test_list_hardware(self):
         results = self.hardware.list_hardware()
 
@@ -96,132 +114,112 @@ def test_reload(self):
                                        'sshKeyIds': [1701]}),
                                 identifier=1)
 
-    def test_get_bare_metal_create_options_returns_none_on_error(self):
-        mock = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
-        mock.return_value = [{
-            'id': 0,
-            'name': 'No Matching Instances',
-            'description': 'Nothing'
-        }]
+    def test_get_create_options(self):
+        options = self.hardware.get_create_options()
+
+        expected = {
+            'extras': [{'key': '1_IPV6_ADDRESS', 'name': '1 IPv6 Address'}],
+            'locations': [{'key': 'wdc01', 'name': 'Washington 1'}],
+            'operating_systems': [{'key': 'UBUNTU_14_64',
+                                   'name': 'Ubuntu / 14.04-64'}],
+            'port_speeds': [{
+                'key': '10',
+                'name': '10 Mbps Public & Private Network Uplinks'
+            }],
+            'sizes': [{
+                'key': 'S1270_8GB_2X1TBSATA_NORAID',
+                'name': 'Single Xeon 1270, 8GB Ram, 2x1TB SATA disks, Non-RAID'
+            }]
+        }
+
+        self.assertEqual(options, expected)
 
-        self.assertIsNone(self.hardware.get_bare_metal_create_options())
+    def test_get_create_options_package_missing(self):
+        packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        packages.return_value = []
 
-    def test_get_bare_metal_create_options(self):
-        result = self.hardware.get_bare_metal_create_options()
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               self.hardware.get_create_options)
+        self.assertEqual("Ordering package not found", str(ex))
 
-        self.assertEqual(len(result['categories']['disk0']['items']), 2)
-        self.assertEqual(len(result['categories']['disk1']['items']), 1)
-        self.assertEqual(len(result['categories']['disk1']['items']), 1)
-        self.assertEqual(len(result['categories']['disk_controller']['items']),
-                         2)
-        self.assertEqual(len(result['categories']['os']['items']), 11)
-        self.assertEqual(len(result['categories']['port_speed']['items']), 5)
-        self.assertEqual(len(result['categories']['ram']['items']), 2)
-        self.assertEqual(len(result['categories']['random']['items']), 1)
-        self.assertEqual(len(result['categories']['server']['items']), 2)
-        self.assertEqual(len(result['categories']['server_core']['items']), 3)
-        self.assertEqual(len(result['locations']), 1)
+    def test_generate_create_dict_no_items(self):
+        packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        packages_copy = copy.deepcopy(
+            fixtures.SoftLayer_Product_Package.getAllObjects)
+        packages_copy[0]['items'] = []
+        packages.return_value = packages_copy
 
-        self.assert_called_with('SoftLayer_Product_Package', 'getRegions',
-                                identifier=50)
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               self.hardware._generate_create_dict)
+        self.assertIn("Could not find valid price", str(ex))
 
-        self.assert_called_with('SoftLayer_Product_Package',
-                                'getConfiguration',
-                                identifier=50,
-                                mask='mask[itemCategory[group]]')
+    def test_generate_create_dict_no_regions(self):
+        packages = self.set_mock('SoftLayer_Product_Package', 'getAllObjects')
+        packages_copy = copy.deepcopy(
+            fixtures.SoftLayer_Product_Package.getAllObjects)
+        packages_copy[0]['regions'] = []
+        packages.return_value = packages_copy
 
-        self.assert_called_with('SoftLayer_Product_Package', 'getCategories',
-                                identifier=50)
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               self.hardware._generate_create_dict,
+                               **MINIMAL_TEST_CREATE_ARGS)
+        self.assertIn("Could not find valid location for: 'wdc01'", str(ex))
 
-    def test_generate_create_dict_with_all_bare_metal_options(self):
+    def test_generate_create_dict_invalid_size(self):
         args = {
-            'server': 100,
+            'size': 'UNKNOWN_SIZE',
             'hostname': 'unicorn',
             'domain': 'giggles.woo',
-            'disks': [500],
-            'location': 'Wyrmshire',
-            'os': 200,
-            'port_speed': 600,
-            'bare_metal': True,
-            'hourly': True,
-            'public_vlan': 10234,
-            'private_vlan': 20468,
+            'location': 'wdc01',
+            'os': 'UBUNTU_14_64',
+            'port_speed': 10,
         }
 
-        expected = {
-            'hardware': [
-                {
-                    'domain': 'giggles.woo',
-                    'bareMetalInstanceFlag': True,
-                    'hostname': 'unicorn',
-                    'primaryBackendNetworkComponent':
-                    {'networkVlan': {'id': 20468}},
-                    'primaryNetworkComponent':
-                    {'networkVlan': {'id': 10234}},
-                }
-            ],
-            'prices': [
-                {'id': 100},
-                {'id': 500},
-                {'id': 200},
-                {'id': 600},
-                {'id': 12000}
-            ],
-            'useHourlyPricing': True,
-            'location': 'Wyrmshire', 'packageId': 50
-        }
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               self.hardware._generate_create_dict, **args)
+        self.assertIn("Could not find valid size for: 'UNKNOWN_SIZE'", str(ex))
 
-        data = self.hardware._generate_create_dict(**args)
-
-        self.assertEqual(expected, data)
-
-    def test_generate_create_dict_with_all_dedicated_server_options(self):
+    def test_generate_create_dict(self):
         args = {
-            'server': 100,
+            'size': 'S1270_8GB_2X1TBSATA_NORAID',
             'hostname': 'unicorn',
             'domain': 'giggles.woo',
-            'disks': [1000, 1000, 1000, 1000],
-            'location': 'Wyrmshire',
-            'os': 200,
-            'port_speed': 600,
-            'bare_metal': False,
-            'package_id': 13,
-            'ram': 1400,
-            'disk_controller': 1500,
-            'ssh_keys': [3000, 3001],
+            'location': 'wdc01',
+            'os': 'UBUNTU_14_64',
+            'port_speed': 10,
+            'hourly': True,
             'public_vlan': 10234,
             'private_vlan': 20468,
-            'post_uri': 'http://somescript.foo/myscript.sh',
+            'extras': ['1_IPV6_ADDRESS'],
+            'post_uri': 'http://example.com/script.php',
+            'ssh_keys': [10],
         }
 
         expected = {
-            'hardware': [
-                {
-                    'domain': 'giggles.woo',
-                    'bareMetalInstanceFlag': False,
-                    'hostname': 'unicorn',
-                    'primaryBackendNetworkComponent':
-                    {'networkVlan': {'id': 20468}},
-                    'primaryNetworkComponent':
-                    {'networkVlan': {'id': 10234}},
-                }
-            ],
-            'prices': [
-                {'id': 100},
-                {'id': 1000},
-                {'id': 1000},
-                {'id': 1000},
-                {'id': 1000},
-                {'id': 200},
-                {'id': 600},
-                {'id': 1400},
-                {'id': 1500}],
-            'sshKeys': [{'sshKeyIds': [3000, 3001]}],
-            'location': 'Wyrmshire', 'packageId': 13,
-            'provisionScripts': ['http://somescript.foo/myscript.sh'],
+            'hardware': [{
+                'domain': 'giggles.woo',
+                'hostname': 'unicorn',
+                'primaryBackendNetworkComponent': {
+                    'networkVlan': {'id': 20468}
+                },
+                'primaryNetworkComponent': {'networkVlan': {'id': 10234}}}],
+            'location': 'WASHINGTON_DC',
+            'packageId': 200,
+            'presetId': 64,
+            'prices': [{'id': 21},
+                       {'id': 420},
+                       {'id': 906},
+                       {'id': 37650},
+                       {'id': 1800},
+                       {'id': 272},
+                       {'id': 17129}],
+            'useHourlyPricing': True,
+            'provisionScripts': ['http://example.com/script.php'],
+            'sshKeys': [{'sshKeyIds': [10]}],
         }
 
         data = self.hardware._generate_create_dict(**args)
+
         self.assertEqual(expected, data)
 
     @mock.patch('SoftLayer.managers.hardware.HardwareManager'
@@ -313,135 +311,6 @@ def test_change_port_speed_private(self):
                                 identifier=2,
                                 args=(10,))
 
-    def test_get_available_dedicated_server_packages(self):
-        self.hardware.get_available_dedicated_server_packages()
-
-        _filter = {
-            'type': {
-                'keyName': {
-                    'operation': 'in',
-                    'options': [{
-                        'name': 'data',
-                        'value': ['BARE_METAL_CPU',
-                                  'BARE_METAL_CORE']
-                    }]
-                }
-            }
-        }
-        self.assert_called_with('SoftLayer_Product_Package', 'getAllObjects',
-                                filter=_filter,
-                                mask='id,name,description,type,isActive')
-
-    def test_get_server_packages_with_ordering_manager_provided(self):
-        self.hardware = SoftLayer.HardwareManager(
-            self.client, SoftLayer.OrderingManager(self.client))
-        self.test_get_available_dedicated_server_packages()
-
-    def test_get_dedicated_server_options(self):
-        result = self.hardware.get_dedicated_server_create_options(13)
-
-        self.assertEqual(len(result['categories']['disk0']['items']), 2)
-        self.assertEqual(len(result['categories']['disk1']['items']), 1)
-        self.assertEqual(len(result['categories']['disk1']['items']), 1)
-        self.assertEqual(len(result['categories']['disk_controller']['items']),
-                         2)
-        self.assertEqual(len(result['categories']['os']['items']), 11)
-        self.assertEqual(len(result['categories']['port_speed']['items']), 5)
-        self.assertEqual(len(result['categories']['ram']['items']), 2)
-        self.assertEqual(len(result['categories']['random']['items']), 1)
-        self.assertEqual(len(result['categories']['server']['items']), 2)
-        self.assertEqual(len(result['categories']['server_core']['items']), 3)
-        self.assertEqual(len(result['locations']), 1)
-
-        self.assert_called_with('SoftLayer_Product_Package', 'getRegions',
-                                identifier=13)
-
-        self.assert_called_with('SoftLayer_Product_Package',
-                                'getConfiguration',
-                                identifier=13,
-                                mask='mask[itemCategory[group]]')
-
-        self.assert_called_with('SoftLayer_Product_Package', 'getCategories',
-                                identifier=13)
-
-    def test_get_default_value_returns_none_for_unknown_category(self):
-        package_options = {'categories': ['Cat1', 'Cat2']}
-
-        self.assertEqual(None, hardware.get_default_value(package_options,
-                                                          'Unknown Category'))
-
-    def test_get_default_value(self):
-        price_id = 9876
-        package_options = {'categories':
-                           {'Cat1': {
-                               'items': [{'setup_fee': 0,
-                                          'recurring_fee': 0,
-                                          'hourly_recurring_fee': 0,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': price_id}]
-                           }}}
-
-        self.assertEqual(price_id,
-                         hardware.get_default_value(package_options, 'Cat1'))
-
-    def test_get_default_value_none_free(self):
-        package_options = {'categories': {}}
-        self.assertEqual(None,
-                         hardware.get_default_value(package_options, 'Cat1'))
-
-        package_options = {'categories':
-                           {'Cat1': {
-                               'items': [{'setup_fee': 10,
-                                          'recurring_fee': 0,
-                                          'hourly_recurring_fee': 0,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': 1234}]
-                           }}}
-        self.assertEqual(None,
-                         hardware.get_default_value(package_options, 'Cat1'))
-
-    def test_get_default_value_hourly(self):
-        package_options = {'categories':
-                           {'Cat1': {
-                               'items': [{'setup_fee': 0,
-                                          'recurring_fee': 0,
-                                          'hourly_recurring_fee': None,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': 1234},
-                                         {'setup_fee': 0,
-                                          'recurring_fee': None,
-                                          'hourly_recurring_fee': 0,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': 4321}]
-                           }}}
-        result = hardware.get_default_value(package_options, 'Cat1',
-                                            hourly=True)
-        self.assertEqual(4321, result)
-
-    def test_get_default_value_monthly(self):
-        package_options = {'categories':
-                           {'Cat1': {
-                               'items': [{'setup_fee': 0,
-                                          'recurring_fee': None,
-                                          'hourly_recurring_fee': 0,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': 4321},
-                                         {'setup_fee': 0,
-                                          'recurring_fee': 0,
-                                          'hourly_recurring_fee': None,
-                                          'one_time_fee': 0,
-                                          'labor_fee': 0,
-                                          'price_id': 1234}]
-                           }}}
-        result = hardware.get_default_value(package_options, 'Cat1',
-                                            hourly=False)
-        self.assertEqual(1234, result)
-
     def test_edit_meta(self):
         # Test editing user data
         self.hardware.edit(100, userdata='my data')
@@ -497,3 +366,56 @@ def test_update_firmware_selective(self):
         self.assert_called_with('SoftLayer_Hardware_Server',
                                 'createFirmwareUpdateTransaction',
                                 identifier=100, args=(0, 1, 1, 0))
+
+
+class HardwareHelperTests(testing.TestCase):
+    def test_get_extra_price_id_no_items(self):
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_extra_price_id,
+                               [], 'test', True)
+        self.assertEqual("Could not find valid price for extra option, 'test'",
+                         str(ex))
+
+    def test_get_default_price_id_item_not_first(self):
+        items = [{
+            'itemCategory': {'categoryCode': 'unknown', 'id': 325},
+            'keyName': 'UNKNOWN',
+            'prices': [{'accountRestrictions': [],
+                        'currentPriceFlag': '',
+                        'hourlyRecurringFee': '10.0',
+                        'id': 1245172,
+                        'recurringFee': '1.0'}],
+        }]
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_default_price_id,
+                               items, 'unknown', True)
+        self.assertEqual("Could not find valid price for 'unknown' option",
+                         str(ex))
+
+    def test_get_default_price_id_no_items(self):
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_default_price_id,
+                               [], 'test', True)
+        self.assertEqual("Could not find valid price for 'test' option",
+                         str(ex))
+
+    def test_get_bandwidth_price_id_no_items(self):
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_bandwidth_price_id,
+                               [], hourly=True, no_public=False)
+        self.assertEqual("Could not find valid price for bandwidth option",
+                         str(ex))
+
+    def test_get_os_price_id_no_items(self):
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_os_price_id,
+                               [], 'UBUNTU_14_64')
+        self.assertEqual("Could not find valid price for os: 'UBUNTU_14_64'",
+                         str(ex))
+
+    def test_get_port_speed_price_id_no_items(self):
+        ex = self.assertRaises(SoftLayer.SoftLayerError,
+                               managers.hardware._get_port_speed_price_id,
+                               [], 10, True)
+        self.assertEqual("Could not find valid price for port speed: '10'",
+                         str(ex))