1"""Order and Cancel LBaaS instances."""
2import click
3
4import SoftLayer
5from SoftLayer.CLI import environment
6from SoftLayer.CLI import exceptions
7from SoftLayer.CLI import formatting
8from SoftLayer.exceptions import SoftLayerAPIError
9from SoftLayer import utils
10
11
12# pylint: disable=unused-argument
13def parse_proto(ctx, param, value):
14    """Parses the frontend and backend cli options"""
15    proto = {'protocol': 'HTTP', 'port': 80}
16    splitout = value.split(':')
17    if len(splitout) != 2:
18        raise exceptions.ArgumentError("{}={} is not properly formatted.".format(param, value))
19    proto['protocol'] = splitout[0]
20    proto['port'] = int(splitout[1])
21    return proto
22
23
24@click.command()
25@click.option('--name', '-n', help='Label for this loadbalancer.', required=True)
26@click.option('--datacenter', '-d', help='Datacenter shortname (dal13).', required=True)
27@click.option('--label', '-l', help='A descriptive label for this loadbalancer.')
28@click.option('--frontend', '-f', required=True, default='HTTP:80', show_default=True, callback=parse_proto,
29              help='PROTOCOL:PORT string for incoming internet connections.')
30@click.option('--backend', '-b', required=True, default='HTTP:80', show_default=True, callback=parse_proto,
31              help='PROTOCOL:PORT string for connecting to backend servers.')
32@click.option('--method', '-m', help="Balancing Method.", default='ROUNDROBIN', show_default=True,
33              type=click.Choice(['ROUNDROBIN', 'LEASTCONNECTION', 'WEIGHTED_RR']))
34@click.option('--subnet', '-s', required=True,
35              help="Private subnet Id to order the LB on. See `slcli lb order-options`")
36@click.option('--public', is_flag=True, default=False, show_default=True, help="Use a Public to Public loadbalancer.")
37@click.option('--verify', is_flag=True, default=False, show_default=True,
38              help="Only verify an order, dont actually create one.")
39@environment.pass_env
40def order(env, **args):
41    """Creates a LB. Protocols supported are TCP, HTTP, and HTTPS."""
42
43    mgr = SoftLayer.LoadBalancerManager(env.client)
44
45    location = args.get('datacenter')
46    name = args.get('name')
47    description = args.get('label', None)
48
49    backend = args.get('backend')
50    frontend = args.get('frontend')
51    protocols = [
52        {
53            "backendPort": backend.get('port'),
54            "backendProtocol": backend.get('protocol'),
55            "frontendPort": frontend.get('port'),
56            "frontendProtocol": frontend.get('protocol'),
57            "loadBalancingMethod": args.get('method'),
58            "maxConn": 1000
59        }
60    ]
61
62    # remove verify=True to place the order
63    receipt = mgr.order_lbaas(location, name, description, protocols, args.get('subnet'),
64                              public=args.get('public'), verify=args.get('verify'))
65    table = parse_receipt(receipt)
66    env.fout(table)
67
68
69def parse_receipt(receipt):
70    """Takes an order receipt and nicely formats it for cli output"""
71    table = formatting.KeyValueTable(['Item', 'Cost'], title="Order: {}".format(receipt.get('orderId', 'Quote')))
72    if receipt.get('prices'):
73        for price in receipt.get('prices'):
74            table.add_row([price['item']['description'], price['hourlyRecurringFee']])
75    elif receipt.get('orderDetails'):
76        for price in receipt['orderDetails']['prices']:
77            table.add_row([price['item']['description'], price['hourlyRecurringFee']])
78
79    return table
80
81
82@click.command()
83@click.option('--datacenter', '-d', help="Show only selected datacenter, use shortname (dal13) format.")
84@environment.pass_env
85def order_options(env, datacenter):
86    """Prints options for order a LBaaS"""
87    print("Prints options for ordering")
88    mgr = SoftLayer.LoadBalancerManager(env.client)
89    net_mgr = SoftLayer.NetworkManager(env.client)
90    package = mgr.lbaas_order_options()
91
92    if not datacenter:
93        data_table = formatting.KeyValueTable(['Datacenters', 'City'])
94        for region in package['regions']:
95            data_table.add_row([region['description'].split('-')[0], region['description'].split('-')[1]])
96            # print(region)
97        env.fout(data_table)
98        click.secho("Use `slcli lb order-options --datacenter <DC>` "
99                    "to find pricing information and private subnets for that specific site.")
100
101    else:
102        for region in package['regions']:
103            dc_name = utils.lookup(region, 'location', 'location', 'name')
104
105            # Skip locations if they are not the one requested.
106            if datacenter and dc_name != datacenter.lower():
107                continue
108
109            l_groups = []
110            for group in region['location']['location']['groups']:
111                l_groups.append(group.get('id'))
112
113            # Price lookups
114            prices = []
115            price_table = formatting.KeyValueTable(['KeyName', 'Cost'], title='Prices')
116            for item in package['items']:
117                i_price = {'keyName': item['keyName']}
118                for price in item.get('prices', []):
119                    if not price.get('locationGroupId'):
120                        i_price['default_price'] = price.get('hourlyRecurringFee')
121                    elif price.get('locationGroupId') in l_groups:
122                        i_price['region_price'] = price.get('hourlyRecurringFee')
123                prices.append(i_price)
124            for price in prices:
125                if price.get('region_price'):
126                    price_table.add_row([price.get('keyName'), price.get('region_price')])
127                else:
128                    price_table.add_row([price.get('keyName'), price.get('default_price')])
129
130            # Vlan/Subnet Lookups
131            mask = "mask[networkVlan,podName,addressSpace]"
132            subnets = net_mgr.list_subnets(datacenter=dc_name, network_space='PRIVATE', mask=mask)
133            subnet_table = formatting.Table(['Id', 'Subnet', 'Vlan'], title='Private subnet')
134
135            for subnet in subnets:
136                # Only show these types, easier to filter here than in an API call.
137                if subnet.get('subnetType') != 'PRIMARY' and \
138                        subnet.get('subnetType') != 'ADDITIONAL_PRIMARY':
139                    continue
140                space = "{}/{}".format(subnet.get('networkIdentifier'), subnet.get('cidr'))
141                vlan = "{}.{}".format(subnet['podName'], subnet['networkVlan']['vlanNumber'])
142                subnet_table.add_row([subnet.get('id'), space, vlan])
143
144            env.fout(price_table)
145            env.fout(subnet_table)
146
147
148@click.command()
149@click.argument('identifier')
150@environment.pass_env
151def cancel(env, identifier):
152    """Cancels a LBaaS instance"""
153
154    mgr = SoftLayer.LoadBalancerManager(env.client)
155    uuid, _ = mgr.get_lbaas_uuid_id(identifier)
156
157    try:
158        mgr.cancel_lbaas(uuid)
159        click.secho("LB {} canceled succesfully.".format(identifier), fg='green')
160    except SoftLayerAPIError as exception:
161        click.secho("ERROR: {}".format(exception.faultString), fg='red')
162