diff --git a/api-daemon/pvcapid/flaskapi.py b/api-daemon/pvcapid/flaskapi.py index ff1f7843..81b08bb5 100755 --- a/api-daemon/pvcapid/flaskapi.py +++ b/api-daemon/pvcapid/flaskapi.py @@ -260,17 +260,27 @@ api.add_resource(API_Logout, '/logout') # /initialize class API_Initialize(Resource): @RequestParser([ - {'name': 'yes-i-really-mean-it', 'required': True, 'helptext': "Initialization is destructive; please confirm with the argument 'yes-i-really-mean-it'."} + {'name': 'overwrite', 'required': False}, + {'name': 'yes-i-really-mean-it', 'required': True, 'helptext': "Initialization is destructive; please confirm with the argument 'yes-i-really-mean-it'."}, ]) @Authenticator def post(self, reqargs): """ Initialize a new PVC cluster - Note: Normally used only once during cluster bootstrap; checks for the existence of the "/primary_node" key before proceeding and returns 400 if found + + If the 'overwrite' option is not True, the cluster will return 400 if the `/primary_node` key is found. If 'overwrite' is True, the existing cluster + data will be erased and new, empty data written in its place. + + All node daemons should be stopped before running this command, and the API daemon started manually to avoid undefined behavior. --- tags: - root parameters: + - in: query + name: overwrite + type: bool + required: false + description: A flag to enable or disable (default) overwriting existing data - in: query name: yes-i-really-mean-it type: string @@ -289,7 +299,10 @@ class API_Initialize(Resource): 400: description: Bad request """ - if api_helper.initialize_cluster(): + if reqargs.get('overwrite', False): + overwrite_flag = True + + if api_helper.initialize_cluster(overwrite=overwrite_flag): return {"message": "Successfully initialized a new PVC cluster"}, 200 else: return {"message": "PVC cluster already initialized"}, 400 diff --git a/api-daemon/pvcapid/helper.py b/api-daemon/pvcapid/helper.py index 9d4614da..3f3f5b0a 100755 --- a/api-daemon/pvcapid/helper.py +++ b/api-daemon/pvcapid/helper.py @@ -41,16 +41,41 @@ import daemon_lib.ceph as pvc_ceph # Cluster base functions # @ZKConnection(config) -def initialize_cluster(zkhandler): +def initialize_cluster(zkhandler, overwrite=False): """ Initialize a new cluster """ # Abort if we've initialized the cluster before - if zkhandler.exists('/primary_node'): + if zkhandler.exists('/primary_node') and not overwrite: return False + if overwrite: + # Delete the existing keys; ignore any errors + status = zkhandler.delete([ + '/primary_node', + '/upstream_ip', + '/maintenance', + '/nodes', + '/domains', + '/networks', + '/ceph', + '/ceph/osds', + '/ceph/pools', + '/ceph/volumes', + '/ceph/snapshots', + '/cmd', + '/cmd/domains', + '/cmd/ceph', + '/locks', + '/locks/flush_lock', + '/locks/primary_node' + ], recursive=True) + + if not status: + return False + # Create the root keys - zkhandler.write([ + status = zkhandler.write([ ('/primary_node', 'none'), ('/upstream_ip', 'none'), ('/maintenance', 'False'), @@ -70,7 +95,7 @@ def initialize_cluster(zkhandler): ('/locks/primary_node', ''), ]) - return True + return status @ZKConnection(config) diff --git a/client-cli/cli_lib/cluster.py b/client-cli/cli_lib/cluster.py index 564ca172..3dff78be 100644 --- a/client-cli/cli_lib/cluster.py +++ b/client-cli/cli_lib/cluster.py @@ -25,16 +25,17 @@ import cli_lib.ansiprint as ansiprint from cli_lib.common import call_api -def initialize(config): +def initialize(config, overwrite=False): """ Initialize the PVC cluster API endpoint: GET /api/v1/initialize - API arguments: yes-i-really-mean-it + API arguments: overwrite, yes-i-really-mean-it API schema: {json_data_object} """ params = { - 'yes-i-really-mean-it': 'yes' + 'yes-i-really-mean-it': 'yes', + 'overwrite': overwrite } response = call_api(config, 'post', '/initialize', params=params) diff --git a/client-cli/pvc.py b/client-cli/pvc.py index 27a803e4..fc79b27d 100755 --- a/client-cli/pvc.py +++ b/client-cli/pvc.py @@ -4304,15 +4304,26 @@ def task_restore(filename, confirm_flag): # pvc task init ############################################################################### @click.command(name='init', short_help='Initialize a new cluster.') +@click.option( + '-o', '--overwite', 'overwrite_flag', + is_flag=True, default=False, + help='Remove and overwrite any existing data' +) @click.option( '-y', '--yes', 'confirm_flag', is_flag=True, default=False, help='Confirm the initialization' ) @cluster_req -def task_init(confirm_flag): +def task_init(confirm_flag, overwrite_flag): """ Perform initialization of a new PVC cluster. + + If the '-o'/'--overwrite' option is specified, all existing data in the cluster will be deleted + before new, empty data is written. + + It is not advisable to do this against a running cluster - all node daemons should be stopped + first and the API daemon started manually before running this command. """ if not confirm_flag and not config['unsafe']: @@ -4324,7 +4335,7 @@ def task_init(confirm_flag): # Easter-egg click.echo("Some music while we're Layin' Pipe? https://youtu.be/sw8S_Kv89IU") - retcode, retmsg = pvc_cluster.initialize(config) + retcode, retmsg = pvc_cluster.initialize(config, overwrite_flag) cleanup(retcode, retmsg)