blob: a7f09a3ddf546c1b9d66a33cc6089436000b480f [file] [log] [blame]
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2019, Xiaomi, Inc. All rights reserved.
# This source code is licensed under the Apache License Version 2.0, which
# can be found in the LICENSE file in the root directory of this source tree.
"""
HOWTO
=====
./scripts/create_table.py --table ai_user_info \
--depart 云平台部-存储平台-KV系统组 \
--user wutao1&qinzuoyan \
--cluster bj1-ai \
--write_throttling "2000*delay*100" \
--partition_count 16
OR
./scripts/create_table.py -t ai_user_info \
-d 云平台部-存储平台-KV系统组 \
-u wutao1&qinzuoyan \
-c bj1-ai \
-w "2000*delay*100" \
-p 16
DEVLOPER GUIDE
==============
The source code is formatted using autopep8.
Ensure you have run formatter before committing changes.
```
autopep8 -i --aggressive --aggressive scripts/create_table.py
```
TODO(wutao1): automatically set write throttling according to the given
estimated QPS on the table.
"""
import os
import click
import py_utils
import re
import json
import math
def validate_param_table(ctx, param, value):
# TODO(wutao1): check illegal characters
return value.encode('utf-8')
def validate_param_depart(ctx, param, value):
return value.encode('utf-8')
def validate_param_user(ctx, param, value):
return value.encode('utf-8')
def validate_param_cluster(ctx, param, value):
return value.encode('utf-8')
def validate_param_partition_count(ctx, param, value):
if value == 0:
raise click.BadParameter("Cannot create table with 0 partition")
if math.log(value, 2) != math.floor(math.log(value, 2)):
raise click.BadParameter(
"Partition count {} should be a power of 2".format(value))
return value
def validate_param_write_throttling(ctx, param, value):
if value == '':
return None
pattern = re.compile(r'^\d+\*delay\*\d+(,\d+\*reject\*\d+)?$')
match = pattern.match(value)
if match is not None:
return value.encode('utf-8')
else:
raise click.BadParameter(
'invalid value of throttle \'%s\'' % value)
def create_table_if_needed(cluster, table, partition_count):
if not cluster.has_table(table):
try:
# TODO(wutao1): Outputs progress while polling.
py_utils.echo("Creating table {}...".format(table))
cluster.create_table(table, partition_count)
except Exception as err:
py_utils.echo(err, "red")
exit(1)
else:
py_utils.echo("Success: table \"{}\" exists".format(table))
def set_business_info_if_needed(cluster, table, depart, user):
new_business_info = "depart={},user={}".format(depart, user)
set_app_envs_if_needed(cluster, table, 'business.info', new_business_info)
def set_write_throttling_if_needed(cluster, table, new_throttle):
if new_throttle is None:
return
set_app_envs_if_needed(
cluster, table, 'replica.write_throttling', new_throttle)
def set_app_envs_if_needed(cluster, table, env_name, new_env_value):
py_utils.echo("New value of {}={}".format(env_name, new_env_value))
envs = cluster.get_app_envs(table)
if envs is not None and envs.get(env_name) is not None:
old_env_value = envs.get(env_name).encode('utf-8')
if old_env_value is not None:
py_utils.echo("Old value of {}={}".format(env_name, old_env_value))
if old_env_value == new_env_value:
py_utils.echo("Success: {} keeps unchanged".format(env_name))
return
cluster.set_app_envs(table, env_name,
new_env_value)
def all_arguments_to_string(
table,
depart,
user,
cluster,
partition_count,
write_throttling):
return json.dumps({
'table': table,
'depart': depart,
'user': user,
'cluster': cluster,
'partition_count': partition_count,
'write_throttling': write_throttling,
}, sort_keys=True, indent=4, ensure_ascii=False, encoding='utf-8')
@click.command()
@click.option("--table", "-t",
required=True,
callback=validate_param_table,
help="Name of the table you want to create.")
@click.option(
"--depart", "-d",
required=True,
callback=validate_param_depart,
help="Department of the table owner. If there are more than one levels of department, use '-' to concatenate them.")
@click.option(
"--user", "-u",
required=True,
callback=validate_param_user,
help="The table owner. If there are more than one owners, use '&' to concatenate them.")
@click.option("--cluster", "-c",
required=True,
callback=validate_param_cluster,
help="The cluster name. Where you want to place the table.")
@click.option("--partition_count", "-p",
callback=validate_param_partition_count,
help="The partition count of the table. Empty means no create.",
type=int)
@click.option(
"--write_throttling", "-w",
default="",
callback=validate_param_write_throttling,
help="{delay_qps_threshold}*delay*{delay_ms},{reject_qps_threshold}*reject*{delay_ms_before_reject}")
def main(table, depart, user, cluster, partition_count, write_throttling):
if not click.confirm(
"Confirm to create table:\n{}\n".format(
all_arguments_to_string(
table,
depart,
user,
cluster,
partition_count,
write_throttling))):
return
c = py_utils.PegasusCluster(cluster_name=cluster)
create_table_if_needed(c, table, partition_count)
set_business_info_if_needed(c, table, depart, user)
set_write_throttling_if_needed(c, table, write_throttling)
if __name__ == "__main__":
main()