Skip to content

Commit

Permalink
security: add dry run mode for external cluster script
Browse files Browse the repository at this point in the history
Adding dry run mode for external cluster script.
This will add cli argument `--dry-run`. By default
`dry-run` option will be `False`
which means it will only print something like below.
```
sh-4.4$ python3 create-external-cluster-resources.py --dry-run=True
The script is checking conflicting options
The script is fetching cephFS data pool details
The script is getting fsid
The script is fetching ceph mon external data
The script is fetching/creating key for rook external user
The script is fetching mgr services
The script is fetching/creating Ceph CSI keyring for RBD provisioner
The script is fetching/creating Ceph CSI keyring for RBD provisioner
The script is mon endpoint and mon endpoint port
None
The script is checks if connected then shutdown
```

Signed-off-by: subhamkrai <srai@redhat.com>
  • Loading branch information
subhamkrai committed Dec 8, 2021
1 parent 3d69e10 commit 084f27b
Showing 1 changed file with 60 additions and 13 deletions.
73 changes: 60 additions & 13 deletions deploy/examples/create-external-cluster-resources.py
Expand Up @@ -193,6 +193,8 @@ def gen_arg_parser(cls, args_to_parse=None):
help="Ceph Manager prometheus exporter endpoints (comma separated list of <IP> entries of active and standby mgrs)")
output_group.add_argument("--monitoring-endpoint-port", default="", required=False,
help="Ceph Manager prometheus exporter port")
output_group.add_argument("--dry-run", default=False, required=False,
help="Dry run the python script")

upgrade_group = argP.add_argument_group('upgrade')
upgrade_group.add_argument("--upgrade", action='store_true', default=False,
Expand All @@ -205,14 +207,22 @@ def gen_arg_parser(cls, args_to_parse=None):
args_to_parse = sys.argv[1:]
return argP.parse_args(args_to_parse)

def dry_run(self, msg):
if self._arg_parser.dry_run:
print("The script is " + msg)

def validate_rgw_endpoint_tls_cert(self):
if self._arg_parser.dry_run:
return self.dry_run("validating rados gateWay endpoint TLS certificate")
if self._arg_parser.rgw_tls_cert_path:
with open(self._arg_parser.rgw_tls_cert_path, encoding='utf8') as f:
contents = f.read()
return contents.rstrip()

def _check_conflicting_options(self):
if not self._arg_parser.upgrade and not self._arg_parser.rbd_data_pool_name:
if self._arg_parser.dry_run:
return self.dry_run("checking conflicting options")
if not self._arg_parser.upgrade and not self._arg_parser.rbd_data_pool_name and not self._arg_parser.dry_run:
raise ExecutionFailureException(
"Either '--upgrade' or '--rbd-data-pool-name <pool_name>' should be specified")
if self._arg_parser.upgrade and self._arg_parser.rbd_data_pool_name:
Expand All @@ -224,6 +234,8 @@ def _check_conflicting_options(self):
"Please provide an existing user-name through '--run-as-user' (or '-u') flag while upgrading")

def _invalid_endpoint(self, endpoint_str):
if self._arg_parser.dry_run:
return self.dry_run("if checking if endpoint is vaild or not")
try:
ipv4, port = endpoint_str.split(':')
except ValueError:
Expand All @@ -250,6 +262,8 @@ def _invalid_endpoint(self, endpoint_str):
return False

def endpoint_dial(self, endpoint_str, timeout=3, cert=None):
if self._arg_parser.dry_run:
return self.dry_run("trying to connect endpoints")
# if the 'cluster' instance is a dummy one,
# don't try to reach out to the endpoint
if isinstance(self.cluster, DummyRados):
Expand Down Expand Up @@ -303,10 +317,14 @@ def __init__(self, arg_list=None):
self.cluster.connect()

def shutdown(self):
if self._arg_parser.dry_run:
return self.dry_run("checks if connected then shutdown")
if self.cluster.state == "connected":
self.cluster.shutdown()

def get_fsid(self):
if self._arg_parser.dry_run:
return self.dry_run("getting fsid")
return str(self.cluster.get_fsid())

def _common_cmd_json_gen(self, cmd_json):
Expand All @@ -324,6 +342,8 @@ def _common_cmd_json_gen(self, cmd_json):
return ret_val, json_out, err_msg

def get_ceph_external_mon_data(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching ceph mon external data")
cmd_json = {"prefix": "quorum_status", "format": "json"}
ret_val, json_out, err_msg = self._common_cmd_json_gen(cmd_json)
# if there is an unsuccessful attempt,
Expand Down Expand Up @@ -375,6 +395,8 @@ def _convert_hostname_to_ip(self, host_name):
return ip

def get_active_and_standby_mgrs(self):
if self._arg_parser.dry_run:
return "", self.dry_run("mon endpoint and mon endpoint port")
monitoring_endpoint_port = self._arg_parser.monitoring_endpoint_port
monitoring_endpoint_ip_list = self._arg_parser.monitoring_endpoint
standby_mgrs = []
Expand Down Expand Up @@ -446,6 +468,8 @@ def get_active_and_standby_mgrs(self):
return all_mgr_ips_str, monitoring_endpoint_port

def create_cephCSIKeyring_cephFSProvisioner(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching/creating Ceph CSI keyring for cephFS provisioner")
'''
command: ceph auth get-or-create client.csi-cephfs-provisioner mon 'allow r' mgr 'allow rw' osd 'allow rw tag cephfs metadata=*'
'''
Expand All @@ -458,7 +482,7 @@ def create_cephCSIKeyring_cephFSProvisioner(self):
if self._arg_parser.restricted_auth_permission:
if metadata_pool == "":
raise ExecutionFailureException(
"'cephfs_metadata_pool_name' not found, please set the '--cephfs-metadata-pool-name' flag")
"'cephfs_metadata_pool_name' not found, please set the '--cephfs-metadata-pool-name' flag")
cmd_json = {"prefix": "auth get-or-create",
"entity": entity,
"caps": ["mon", "allow r", "mgr", "allow rw",
Expand All @@ -479,6 +503,8 @@ def create_cephCSIKeyring_cephFSProvisioner(self):
return str(json_out[0]['key'])

def create_cephCSIKeyring_cephFSNode(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching/creating Ceph CSI keyring for cephFS node")
data_pool = self._arg_parser.cephfs_data_pool_name
cluster_name = self._arg_parser.cluster_name
entity = "client.csi-cephfs-node"
Expand All @@ -492,9 +518,10 @@ def create_cephCSIKeyring_cephFSNode(self):
cmd_json = {"prefix": "auth get-or-create",
"entity": entity,
"caps": ["mon", "allow r",
"mgr", "allow rw",
"osd", "allow rw tag cephfs data={}".format(data_pool),
"mds", "allow rw"],
"mgr", "allow rw",
"osd", "allow rw tag cephfs data={}".format(
data_pool),
"mds", "allow rw"],
"format": "json"}
else:
cmd_json = {"prefix": "auth get-or-create",
Expand All @@ -513,12 +540,14 @@ def create_cephCSIKeyring_cephFSNode(self):
return str(json_out[0]['key'])

def create_cephCSIKeyring_RBDProvisioner(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching/creating Ceph CSI keyring for RBD provisioner")
rbd_pool_name = self._arg_parser.rbd_data_pool_name
cluster_name = self._arg_parser.cluster_name
entity = "client.csi-rbd-provisioner"
if cluster_name:
entity = "client.csi-rbd-provisioner-{}".format(cluster_name)
cmd_json={}
cmd_json = {}
if self._arg_parser.restricted_auth_permission:
if rbd_pool_name == "":
raise ExecutionFailureException(
Expand All @@ -545,6 +574,8 @@ def create_cephCSIKeyring_RBDProvisioner(self):
return str(json_out[0]['key'])

def get_cephfs_data_pool_details(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching cephFS data pool details")
cmd_json = {"prefix": "fs ls", "format": "json"}
ret_val, json_out, err_msg = self._common_cmd_json_gen(cmd_json)
# if there is an unsuccessful attempt, report an error
Expand Down Expand Up @@ -597,8 +628,10 @@ def get_cephfs_data_pool_details(self):
return

if matching_json_out:
self._arg_parser.cephfs_filesystem_name = str(matching_json_out['name'])
self._arg_parser.cephfs_metadata_pool_name = str(matching_json_out['metadata_pool'])
self._arg_parser.cephfs_filesystem_name = str(
matching_json_out['name'])
self._arg_parser.cephfs_metadata_pool_name = str(
matching_json_out['metadata_pool'])

if type(matching_json_out['data_pools']) == list:
# if the user has already provided data-pool-name,
Expand Down Expand Up @@ -630,12 +663,14 @@ def get_cephfs_data_pool_details(self):
self._arg_parser.cephfs_data_pool_name))

def create_cephCSIKeyring_RBDNode(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching/creating Ceph CSI keyring for RBD provisioner")
rbd_pool_name = self._arg_parser.rbd_data_pool_name
cluster_name = self._arg_parser.cluster_name
entity = "client.csi-rbd-node"
if cluster_name:
entity = "client.csi-rbd-node-{}".format(cluster_name)
cmd_json={}
cmd_json = {}
if self._arg_parser.restricted_auth_permission:
if rbd_pool_name == "":
raise ExecutionFailureException(
Expand All @@ -660,6 +695,8 @@ def create_cephCSIKeyring_RBDNode(self):
return str(json_out[0]['key'])

def create_checkerKey(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching/creating key for rook external user")
cmd_json = {"prefix": "auth get-or-create",
"entity": self.run_as_user,
"caps": ["mon", self.MIN_USER_CAP_PERMISSIONS['mon'],
Expand All @@ -675,6 +712,8 @@ def create_checkerKey(self):
return str(json_out[0]['key'])

def get_ceph_dashboard_link(self):
if self._arg_parser.dry_run:
return self.dry_run("fetching mgr services")
cmd_json = {"prefix": "mgr services", "format": "json"}
ret_val, json_out, _ = self._common_cmd_json_gen(cmd_json)
# if there is an unsuccessful attempt,
Expand All @@ -685,6 +724,8 @@ def get_ceph_dashboard_link(self):
return json_out['dashboard']

def create_rgw_admin_ops_user(self):
if self._arg_parser.dry_run:
return self.dry_run("fetchs/create rgw admin ops user for access and secret key")
cmd = ['radosgw-admin', 'user', 'create', '--uid', self.EXTERNAL_RGW_ADMIN_OPS_USER_NAME, '--display-name',
'Rook RGW Admin Ops user', '--caps', 'buckets=*;users=*;usage=read;metadata=read;zone=read']
try:
Expand Down Expand Up @@ -729,7 +770,7 @@ def _gen_output_map(self):
pools_to_validate.extend(rgw_pool_to_validate)

for pool in pools_to_validate:
if not self.cluster.pool_exists(pool):
if not self.cluster.pool_exists(pool) and not self._arg_parser.dry_run:
raise ExecutionFailureException(
"The provided pool, '{}', does not exist".format(pool))
self._excluded_keys.add('CLUSTER_NAME')
Expand All @@ -751,7 +792,8 @@ def _gen_output_map(self):
self.out_map['CSI_CEPHFS_PROVISIONER_SECRET'] = ''
# create CephFS node and provisioner keyring only when MDS exists
if self.out_map['CEPHFS_FS_NAME'] and self.out_map['CEPHFS_POOL_NAME']:
self.out_map['CSI_CEPHFS_NODE_SECRET'] = self.create_cephCSIKeyring_cephFSNode()
self.out_map['CSI_CEPHFS_NODE_SECRET'] = self.create_cephCSIKeyring_cephFSNode(
)
self.out_map['CSI_CEPHFS_PROVISIONER_SECRET'] = self.create_cephCSIKeyring_cephFSProvisioner()
self.out_map['RGW_ENDPOINT'] = self._arg_parser.rgw_endpoint
self.out_map['RGW_TLS_CERT'] = ''
Expand All @@ -775,6 +817,9 @@ def gen_shell_out(self):
return shOut

def gen_json_out(self):
if self._arg_parser.dry_run:
return self._gen_output_map()

self._gen_output_map()
json_out = [
{
Expand Down Expand Up @@ -1035,14 +1080,16 @@ def test_method_main_output(self):

def test_method_create_cephCSIKeyring_cephFSProvisioner(self):
csiKeyring = self.rjObj.create_cephCSIKeyring_cephFSProvisioner()
print("cephCSIKeyring without restricting it to a metadata pool. {}".format(csiKeyring))
print("cephCSIKeyring without restricting it to a metadata pool. {}".format(
csiKeyring))
self.rjObj._arg_parser.restricted_auth_permission = True
self.rjObj._arg_parser.cephfs_metadata_pool_name = "myfs-metadata"
csiKeyring = self.rjObj.create_cephCSIKeyring_cephFSProvisioner()
print("cephCSIKeyring for a specific metadata pool. {}".format(csiKeyring))
self.rjObj._arg_parser.cluster_name = "openshift-storage"
csiKeyring = self.rjObj.create_cephCSIKeyring_cephFSProvisioner()
print("cephCSIKeyring for a specific metadata pool and cluster. {}".format(csiKeyring))
print("cephCSIKeyring for a specific metadata pool and cluster. {}".format(
csiKeyring))

def test_non_zero_return_and_error(self):
self.rjObj.cluster.return_val = 1
Expand Down

0 comments on commit 084f27b

Please sign in to comment.