|
55 | 55 | DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:9.4.7' |
56 | 56 | DEFAULT_HAPROXY_IMAGE = 'quay.io/ceph/haproxy:2.3' |
57 | 57 | DEFAULT_KEEPALIVED_IMAGE = 'quay.io/ceph/keepalived:2.2.4' |
| 58 | +DEFAULT_NVMEOF_IMAGE = 'quay.io/ceph/nvmeof:0.0.1' |
58 | 59 | DEFAULT_SNMP_GATEWAY_IMAGE = 'docker.io/maxwo/snmp-notifier:v1.2.1' |
59 | 60 | DEFAULT_ELASTICSEARCH_IMAGE = 'quay.io/omrizeneva/elasticsearch:6.8.23' |
60 | 61 | DEFAULT_JAEGER_COLLECTOR_IMAGE = 'quay.io/jaegertracing/jaeger-collector:1.29' |
@@ -393,7 +394,7 @@ class UnauthorizedRegistryError(Error): |
393 | 394 | class Ceph(object): |
394 | 395 | daemons = ('mon', 'mgr', 'osd', 'mds', 'rgw', 'rbd-mirror', |
395 | 396 | 'crash', 'cephfs-mirror', 'ceph-exporter') |
396 | | - gateways = ('iscsi', 'nfs') |
| 397 | + gateways = ('iscsi', 'nfs', 'nvmeof') |
397 | 398 |
|
398 | 399 | ################################## |
399 | 400 |
|
@@ -916,7 +917,8 @@ def get_version(ctx, container_id): |
916 | 917 | version = None |
917 | 918 | out, err, code = call(ctx, |
918 | 919 | [ctx.container_engine.path, 'exec', container_id, |
919 | | - '/usr/bin/python3', '-c', "import pkg_resources; print(pkg_resources.require('ceph_iscsi')[0].version)"], |
| 920 | + '/usr/bin/python3', '-c', |
| 921 | + "import pkg_resources; print(pkg_resources.require('ceph_iscsi')[0].version)"], |
920 | 922 | verbosity=CallVerbosity.QUIET) |
921 | 923 | if code == 0: |
922 | 924 | version = out.strip() |
@@ -983,6 +985,133 @@ def get_tcmu_runner_container(self): |
983 | 985 | tcmu_container.cname = self.get_container_name(desc='tcmu') |
984 | 986 | return tcmu_container |
985 | 987 |
|
| 988 | + |
| 989 | +################################## |
| 990 | + |
| 991 | + |
| 992 | +class CephNvmeof(object): |
| 993 | + """Defines a Ceph-Nvmeof container""" |
| 994 | + |
| 995 | + daemon_type = 'nvmeof' |
| 996 | + required_files = ['ceph-nvmeof.conf'] |
| 997 | + default_image = DEFAULT_NVMEOF_IMAGE |
| 998 | + |
| 999 | + def __init__(self, |
| 1000 | + ctx, |
| 1001 | + fsid, |
| 1002 | + daemon_id, |
| 1003 | + config_json, |
| 1004 | + image=DEFAULT_NVMEOF_IMAGE): |
| 1005 | + # type: (CephadmContext, str, Union[int, str], Dict, str) -> None |
| 1006 | + self.ctx = ctx |
| 1007 | + self.fsid = fsid |
| 1008 | + self.daemon_id = daemon_id |
| 1009 | + self.image = image |
| 1010 | + |
| 1011 | + # config-json options |
| 1012 | + self.files = dict_get(config_json, 'files', {}) |
| 1013 | + |
| 1014 | + # validate the supplied args |
| 1015 | + self.validate() |
| 1016 | + |
| 1017 | + @classmethod |
| 1018 | + def init(cls, ctx, fsid, daemon_id): |
| 1019 | + # type: (CephadmContext, str, Union[int, str]) -> CephNvmeof |
| 1020 | + return cls(ctx, fsid, daemon_id, |
| 1021 | + fetch_configs(ctx), ctx.image) |
| 1022 | + |
| 1023 | + @staticmethod |
| 1024 | + def get_container_mounts(data_dir: str) -> Dict[str, str]: |
| 1025 | + mounts = dict() |
| 1026 | + mounts[os.path.join(data_dir, 'config')] = '/etc/ceph/ceph.conf:z' |
| 1027 | + # mounts[os.path.join(data_dir, 'keyring')] = '/etc/ceph/keyring:z' |
| 1028 | + mounts['/etc/ceph/ceph.client.admin.keyring'] = '/etc/ceph/keyring:z' # TODO: FIXME |
| 1029 | + mounts[os.path.join(data_dir, 'ceph-nvmeof.conf')] = '/src/ceph-nvmeof.conf:z' |
| 1030 | + mounts[os.path.join(data_dir, 'configfs')] = '/sys/kernel/config' |
| 1031 | + mounts['/dev/hugepages'] = '/dev/hugepages' |
| 1032 | + mounts['/dev/vfio/vfio'] = '/dev/vfio/vfio' |
| 1033 | + return mounts |
| 1034 | + |
| 1035 | + @staticmethod |
| 1036 | + def get_container_binds(): |
| 1037 | + # type: () -> List[List[str]] |
| 1038 | + binds = [] |
| 1039 | + lib_modules = ['type=bind', |
| 1040 | + 'source=/lib/modules', |
| 1041 | + 'destination=/lib/modules', |
| 1042 | + 'ro=true'] |
| 1043 | + binds.append(lib_modules) |
| 1044 | + return binds |
| 1045 | + |
| 1046 | + @staticmethod |
| 1047 | + def get_version(ctx: CephadmContext, container_id: str) -> Optional[str]: |
| 1048 | + out, err, ret = call(ctx, |
| 1049 | + [ctx.container_engine.path, 'inspect', |
| 1050 | + '--format', '{{index .Config.Labels "io.ceph.version"}}', |
| 1051 | + ctx.image]) |
| 1052 | + version = None |
| 1053 | + if ret == 0: |
| 1054 | + version = out.strip() |
| 1055 | + return version |
| 1056 | + |
| 1057 | + def validate(self): |
| 1058 | + # type: () -> None |
| 1059 | + if not is_fsid(self.fsid): |
| 1060 | + raise Error('not an fsid: %s' % self.fsid) |
| 1061 | + if not self.daemon_id: |
| 1062 | + raise Error('invalid daemon_id: %s' % self.daemon_id) |
| 1063 | + if not self.image: |
| 1064 | + raise Error('invalid image: %s' % self.image) |
| 1065 | + |
| 1066 | + # check for the required files |
| 1067 | + if self.required_files: |
| 1068 | + for fname in self.required_files: |
| 1069 | + if fname not in self.files: |
| 1070 | + raise Error('required file missing from config-json: %s' % fname) |
| 1071 | + |
| 1072 | + def get_daemon_name(self): |
| 1073 | + # type: () -> str |
| 1074 | + return '%s.%s' % (self.daemon_type, self.daemon_id) |
| 1075 | + |
| 1076 | + def get_container_name(self, desc=None): |
| 1077 | + # type: (Optional[str]) -> str |
| 1078 | + cname = '%s-%s' % (self.fsid, self.get_daemon_name()) |
| 1079 | + if desc: |
| 1080 | + cname = '%s-%s' % (cname, desc) |
| 1081 | + return cname |
| 1082 | + |
| 1083 | + def create_daemon_dirs(self, data_dir, uid, gid): |
| 1084 | + # type: (str, int, int) -> None |
| 1085 | + """Create files under the container data dir""" |
| 1086 | + if not os.path.isdir(data_dir): |
| 1087 | + raise OSError('data_dir is not a directory: %s' % (data_dir)) |
| 1088 | + |
| 1089 | + logger.info('Creating ceph-nvmeof config...') |
| 1090 | + configfs_dir = os.path.join(data_dir, 'configfs') |
| 1091 | + makedirs(configfs_dir, uid, gid, 0o755) |
| 1092 | + |
| 1093 | + # populate files from the config-json |
| 1094 | + populate_files(data_dir, self.files, uid, gid) |
| 1095 | + |
| 1096 | + @staticmethod |
| 1097 | + def configfs_mount_umount(data_dir, mount=True): |
| 1098 | + # type: (str, bool) -> List[str] |
| 1099 | + mount_path = os.path.join(data_dir, 'configfs') |
| 1100 | + if mount: |
| 1101 | + cmd = 'if ! grep -qs {0} /proc/mounts; then ' \ |
| 1102 | + 'mount -t configfs none {0}; fi'.format(mount_path) |
| 1103 | + else: |
| 1104 | + cmd = 'if grep -qs {0} /proc/mounts; then ' \ |
| 1105 | + 'umount {0}; fi'.format(mount_path) |
| 1106 | + return cmd.split() |
| 1107 | + |
| 1108 | + @staticmethod |
| 1109 | + def get_sysctl_settings() -> List[str]: |
| 1110 | + return [ |
| 1111 | + 'vm.nr_hugepages = 4096', |
| 1112 | + ] |
| 1113 | + |
| 1114 | + |
986 | 1115 | ################################## |
987 | 1116 |
|
988 | 1117 |
|
@@ -1435,6 +1564,7 @@ def get_supported_daemons(): |
1435 | 1564 | supported_daemons.extend(Monitoring.components) |
1436 | 1565 | supported_daemons.append(NFSGanesha.daemon_type) |
1437 | 1566 | supported_daemons.append(CephIscsi.daemon_type) |
| 1567 | + supported_daemons.append(CephNvmeof.daemon_type) |
1438 | 1568 | supported_daemons.append(CustomContainer.daemon_type) |
1439 | 1569 | supported_daemons.append(HAproxy.daemon_type) |
1440 | 1570 | supported_daemons.append(Keepalived.daemon_type) |
@@ -2327,6 +2457,8 @@ def update_default_image(ctx: CephadmContext) -> None: |
2327 | 2457 | ctx.image = Keepalived.default_image |
2328 | 2458 | if type_ == SNMPGateway.daemon_type: |
2329 | 2459 | ctx.image = SNMPGateway.default_image |
| 2460 | + if type_ == CephNvmeof.daemon_type: |
| 2461 | + ctx.image = CephNvmeof.default_image |
2330 | 2462 | if type_ in Tracing.components: |
2331 | 2463 | ctx.image = Tracing.components[type_]['image'] |
2332 | 2464 | if not ctx.image: |
@@ -2966,6 +3098,10 @@ def create_daemon_dirs(ctx, fsid, daemon_type, daemon_id, uid, gid, |
2966 | 3098 | ceph_iscsi = CephIscsi.init(ctx, fsid, daemon_id) |
2967 | 3099 | ceph_iscsi.create_daemon_dirs(data_dir, uid, gid) |
2968 | 3100 |
|
| 3101 | + elif daemon_type == CephNvmeof.daemon_type: |
| 3102 | + ceph_nvmeof = CephNvmeof.init(ctx, fsid, daemon_id) |
| 3103 | + ceph_nvmeof.create_daemon_dirs(data_dir, uid, gid) |
| 3104 | + |
2969 | 3105 | elif daemon_type == HAproxy.daemon_type: |
2970 | 3106 | haproxy = HAproxy.init(ctx, fsid, daemon_id) |
2971 | 3107 | haproxy.create_daemon_dirs(data_dir, uid, gid) |
@@ -3144,6 +3280,8 @@ def get_container_binds(ctx, fsid, daemon_type, daemon_id): |
3144 | 3280 |
|
3145 | 3281 | if daemon_type == CephIscsi.daemon_type: |
3146 | 3282 | binds.extend(CephIscsi.get_container_binds()) |
| 3283 | + if daemon_type == CephNvmeof.daemon_type: |
| 3284 | + binds.extend(CephNvmeof.get_container_binds()) |
3147 | 3285 | elif daemon_type == CustomContainer.daemon_type: |
3148 | 3286 | assert daemon_id |
3149 | 3287 | cc = CustomContainer.init(ctx, fsid, daemon_id) |
@@ -3260,6 +3398,11 @@ def get_container_mounts(ctx, fsid, daemon_type, daemon_id, |
3260 | 3398 | data_dir = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id) |
3261 | 3399 | mounts.update(HAproxy.get_container_mounts(data_dir)) |
3262 | 3400 |
|
| 3401 | + if daemon_type == CephNvmeof.daemon_type: |
| 3402 | + assert daemon_id |
| 3403 | + data_dir = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id) |
| 3404 | + mounts.update(CephNvmeof.get_container_mounts(data_dir)) |
| 3405 | + |
3263 | 3406 | if daemon_type == CephIscsi.daemon_type: |
3264 | 3407 | assert daemon_id |
3265 | 3408 | data_dir = get_data_dir(fsid, ctx.data_dir, daemon_type, daemon_id) |
@@ -3394,6 +3537,11 @@ def get_container(ctx: CephadmContext, |
3394 | 3537 | name = '%s.%s' % (daemon_type, daemon_id) |
3395 | 3538 | envs.extend(Keepalived.get_container_envs()) |
3396 | 3539 | container_args.extend(['--cap-add=NET_ADMIN', '--cap-add=NET_RAW']) |
| 3540 | + elif daemon_type == CephNvmeof.daemon_type: |
| 3541 | + name = '%s.%s' % (daemon_type, daemon_id) |
| 3542 | + container_args.extend(['--ulimit', 'memlock=-1:-1']) |
| 3543 | + container_args.extend(['--ulimit', 'nofile=10240']) |
| 3544 | + container_args.extend(['--cap-add=SYS_ADMIN', '--cap-add=CAP_SYS_NICE']) |
3397 | 3545 | elif daemon_type == CephIscsi.daemon_type: |
3398 | 3546 | entrypoint = CephIscsi.entrypoint |
3399 | 3547 | name = '%s.%s' % (daemon_type, daemon_id) |
@@ -3971,6 +4119,8 @@ def _write(conf: Path, lines: List[str]) -> None: |
3971 | 4119 | lines = HAproxy.get_sysctl_settings() |
3972 | 4120 | elif daemon_type == 'keepalived': |
3973 | 4121 | lines = Keepalived.get_sysctl_settings() |
| 4122 | + elif daemon_type == CephNvmeof.daemon_type: |
| 4123 | + lines = CephNvmeof.get_sysctl_settings() |
3974 | 4124 | lines = filter_sysctl_settings(ctx, lines) |
3975 | 4125 |
|
3976 | 4126 | # apply the sysctl settings |
@@ -6445,6 +6595,14 @@ def _dispatch_deploy( |
6445 | 6595 | config=config, keyring=keyring, |
6446 | 6596 | deployment_type=deployment_type, |
6447 | 6597 | ports=daemon_ports) |
| 6598 | + elif daemon_type == CephNvmeof.daemon_type: |
| 6599 | + config, keyring = get_config_and_keyring(ctx) |
| 6600 | + uid, gid = 167, 167 # TODO: need to get properly the uid/gid |
| 6601 | + c = get_deployment_container(ctx, ctx.fsid, daemon_type, daemon_id) |
| 6602 | + deploy_daemon(ctx, ctx.fsid, daemon_type, daemon_id, c, uid, gid, |
| 6603 | + config=config, keyring=keyring, |
| 6604 | + deployment_type=deployment_type, |
| 6605 | + ports=daemon_ports) |
6448 | 6606 | elif daemon_type in Tracing.components: |
6449 | 6607 | uid, gid = 65534, 65534 |
6450 | 6608 | c = get_container(ctx, ctx.fsid, daemon_type, daemon_id) |
@@ -6986,6 +7144,8 @@ def list_daemons(ctx, detail=True, legacy_dir=None): |
6986 | 7144 | version = NFSGanesha.get_version(ctx, container_id) |
6987 | 7145 | if daemon_type == CephIscsi.daemon_type: |
6988 | 7146 | version = CephIscsi.get_version(ctx, container_id) |
| 7147 | + if daemon_type == CephNvmeof.daemon_type: |
| 7148 | + version = CephNvmeof.get_version(ctx, container_id) |
6989 | 7149 | elif not version: |
6990 | 7150 | if daemon_type in Ceph.daemons: |
6991 | 7151 | out, err, code = call(ctx, |
|
0 commit comments