def deploy_osd_daemons_for_existing_osds( self, host: str, service_name: str, replace_osd_ids: Optional[List[str]] = None) -> str: if replace_osd_ids is None: replace_osd_ids = OsdIdClaims(self.mgr).filtered_by_host(host) assert replace_osd_ids is not None # check result osds_elems: dict = CephadmServe(self.mgr)._run_cephadm_json( host, 'osd', 'ceph-volume', [ '--', 'lvm', 'list', '--format', 'json', ]) before_osd_uuid_map = self.mgr.get_osd_uuid_map(only_up=True) fsid = self.mgr._cluster_fsid osd_uuid_map = self.mgr.get_osd_uuid_map() created = [] for osd_id, osds in osds_elems.items(): for osd in osds: if osd['type'] == 'db': continue if osd['tags']['ceph.cluster_fsid'] != fsid: logger.debug('mismatched fsid, skipping %s' % osd) continue if osd_id in before_osd_uuid_map and osd_id not in replace_osd_ids: # if it exists but is part of the replacement operation, don't skip continue if osd_id not in osd_uuid_map: logger.debug( 'osd id {} does not exist in cluster'.format(osd_id)) continue if osd_uuid_map.get(osd_id) != osd['tags']['ceph.osd_fsid']: logger.debug('mismatched osd uuid (cluster has %s, osd ' 'has %s)' % (osd_uuid_map.get(osd_id), osd['tags']['ceph.osd_fsid'])) continue created.append(osd_id) daemon_spec: CephadmDaemonDeploySpec = CephadmDaemonDeploySpec( service_name=service_name, daemon_id=osd_id, host=host, daemon_type='osd', ) daemon_spec.final_config, daemon_spec.deps = self.generate_config( daemon_spec) CephadmServe(self.mgr)._create_daemon( daemon_spec, osd_uuid_map=osd_uuid_map) if created: self.mgr.cache.invalidate_host_devices(host) self.mgr.cache.invalidate_autotune(host) return "Created osd(s) %s on host '%s'" % (','.join(created), host) else: return "Created no osd(s) on host %s; already created?" % host
def test_iscsi_client_caps(self): iscsi_daemon_spec = CephadmDaemonDeploySpec( host='host', daemon_id='a', service_name=self.iscsi_spec.service_name()) self.iscsi_service.prepare_create(iscsi_daemon_spec) expected_caps = [ 'mon', 'profile rbd, allow command "osd blocklist", allow command "config-key get" with "key" prefix "iscsi/"', 'mgr', 'allow command "service status"', 'osd', 'allow rwx' ] expected_call = call({ 'prefix': 'auth get-or-create', 'entity': 'client.iscsi.a', 'caps': expected_caps }) expected_call2 = call({ 'prefix': 'auth caps', 'entity': 'client.iscsi.a', 'caps': expected_caps }) assert expected_call in self.mgr.mon_command.mock_calls assert expected_call2 in self.mgr.mon_command.mock_calls
def test_iscsi_client_caps(self): mgr = FakeMgr() iscsi_service = self._get_services(mgr)['iscsi'] iscsi_spec = IscsiServiceSpec(service_type='iscsi', service_id="a") iscsi_spec.daemon_type = "iscsi" iscsi_spec.daemon_id = "a" iscsi_spec.spec = MagicMock() iscsi_spec.spec.daemon_type = "iscsi" iscsi_spec.spec.ssl_cert = '' mgr.spec_store = MagicMock() mgr.spec_store.__getitem__.return_value = iscsi_spec iscsi_daemon_spec = CephadmDaemonDeploySpec( host='host', daemon_id='a', service_name=iscsi_spec.service_name()) iscsi_service.prepare_create(iscsi_daemon_spec) expected_caps = ['mon', 'profile rbd, allow command "osd blocklist", allow command "config-key get" with "key" prefix "iscsi/"', 'mgr', 'allow command "service status"', 'osd', 'allow rwx'] expected_call = call({'prefix': 'auth get-or-create', 'entity': 'client.iscsi.a', 'caps': expected_caps}) expected_call2 = call({'prefix': 'auth caps', 'entity': 'client.iscsi.a', 'caps': expected_caps}) assert expected_call in mgr.mon_command.mock_calls assert expected_call2 in mgr.mon_command.mock_calls
def test_grafana_initial_admin_pw(self, cephadm_module: CephadmOrchestrator): with with_host(cephadm_module, 'test'): with with_service(cephadm_module, ServiceSpec('mgr')) as _, \ with_service(cephadm_module, GrafanaSpec(initial_admin_password='******')): out = cephadm_module.cephadm_services[ 'grafana'].generate_config( CephadmDaemonDeploySpec('test', 'daemon', 'grafana')) assert out == ({ 'files': { 'grafana.ini': '# This file is generated by cephadm.\n' '[users]\n' ' default_theme = light\n' '[auth.anonymous]\n' ' enabled = true\n' " org_name = 'Main Org.'\n" " org_role = 'Viewer'\n" '[server]\n' " domain = 'bootstrap.storage.lab'\n" ' protocol = https\n' ' cert_file = /etc/grafana/certs/cert_file\n' ' cert_key = /etc/grafana/certs/cert_key\n' ' http_port = 3000\n' ' http_addr = \n' '[security]\n' ' admin_user = admin\n' ' admin_password = secure\n' ' cookie_secure = true\n' ' cookie_samesite = none\n' ' allow_embedding = true', 'provisioning/datasources/ceph-dashboard.yml': "# This file is generated by cephadm.\n" 'deleteDatasources:\n\n' " - name: 'Loki'\n" ' orgId: 2\n\n' 'datasources:\n\n' " - name: 'Loki'\n" " type: 'loki'\n" " access: 'proxy'\n" ' orgId: 2\n' " url: 'http://[1::4]:3100'\n" ' basicAuth: false\n' ' isDefault: true\n' ' editable: false', 'certs/cert_file': ANY, 'certs/cert_key': ANY } }, [])
def test_ingress_config(self, _run_cephadm, cephadm_module: CephadmOrchestrator): _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) with with_host(cephadm_module, 'test'): cephadm_module.cache.update_host_networks( 'test', {'1.2.3.0/24': { 'if0': ['1.2.3.4/32'] }}) # the ingress backend s = RGWSpec(service_id="foo", placement=PlacementSpec(count=1), rgw_frontend_type='beast') ispec = IngressSpec(service_type='ingress', service_id='test', backend_service='rgw.foo', frontend_port=8089, monitor_port=8999, monitor_user='******', monitor_password='******', keepalived_password='******', virtual_interface_networks=['1.2.3.0/24'], virtual_ip="1.2.3.4/32") with with_service(cephadm_module, s) as _, with_service(cephadm_module, ispec) as _: # generate the keepalived conf based on the specified spec keepalived_generated_conf = cephadm_module.cephadm_services[ 'ingress'].keepalived_generate_config( CephadmDaemonDeploySpec( host='test', daemon_id='ingress', service_name=ispec.service_name())) keepalived_expected_conf = { 'files': { 'keepalived.conf': '# This file is generated by cephadm.\n' 'vrrp_script check_backend {\n ' 'script "/usr/bin/curl http://localhost:8999/health"\n ' 'weight -20\n ' 'interval 2\n ' 'rise 2\n ' 'fall 2\n}\n\n' 'vrrp_instance VI_0 {\n ' 'state MASTER\n ' 'priority 100\n ' 'interface if0\n ' 'virtual_router_id 51\n ' 'advert_int 1\n ' 'authentication {\n ' 'auth_type PASS\n ' 'auth_pass 12345\n ' '}\n ' 'unicast_src_ip 1::4\n ' 'unicast_peer {\n ' '}\n ' 'virtual_ipaddress {\n ' '1.2.3.4/32 dev if0\n ' '}\n ' 'track_script {\n ' 'check_backend\n }\n' '}' } } # check keepalived config assert keepalived_generated_conf[0] == keepalived_expected_conf # generate the haproxy conf based on the specified spec haproxy_generated_conf = cephadm_module.cephadm_services[ 'ingress'].haproxy_generate_config( CephadmDaemonDeploySpec( host='test', daemon_id='ingress', service_name=ispec.service_name())) haproxy_expected_conf = { 'files': { 'haproxy.cfg': '# This file is generated by cephadm.' '\nglobal\n log ' '127.0.0.1 local2\n ' 'chroot /var/lib/haproxy\n ' 'pidfile /var/lib/haproxy/haproxy.pid\n ' 'maxconn 8000\n ' 'daemon\n ' 'stats socket /var/lib/haproxy/stats\n' '\ndefaults\n ' 'mode http\n ' 'log global\n ' 'option httplog\n ' 'option dontlognull\n ' 'option http-server-close\n ' 'option forwardfor except 127.0.0.0/8\n ' 'option redispatch\n ' 'retries 3\n ' 'timeout queue 20s\n ' 'timeout connect 5s\n ' 'timeout http-request 1s\n ' 'timeout http-keep-alive 5s\n ' 'timeout client 1s\n ' 'timeout server 1s\n ' 'timeout check 5s\n ' 'maxconn 8000\n' '\nfrontend stats\n ' 'mode http\n ' 'bind 1.2.3.4:8999\n ' 'bind localhost:8999\n ' 'stats enable\n ' 'stats uri /stats\n ' 'stats refresh 10s\n ' 'stats auth admin:12345\n ' 'http-request use-service prometheus-exporter if { path /metrics }\n ' 'monitor-uri /health\n' '\nfrontend frontend\n ' 'bind 1.2.3.4:8089\n ' 'default_backend backend\n\n' 'backend backend\n ' 'option forwardfor\n ' 'balance static-rr\n ' 'option httpchk HEAD / HTTP/1.0\n ' 'server ' + haproxy_generated_conf[1][0] + ' 1::4:80 check weight 100\n' } } assert haproxy_generated_conf[0] == haproxy_expected_conf
def create_single_host(self, drive_group: DriveGroupSpec, host: str, cmd: str, replace_osd_ids: List[str], env_vars: Optional[List[str]] = None) -> str: out, err, code = self._run_ceph_volume_command(host, cmd, env_vars=env_vars) if code == 1 and ', it is already prepared' in '\n'.join(err): # HACK: when we create against an existing LV, ceph-volume # returns an error and the above message. To make this # command idempotent, tolerate this "error" and continue. logger.debug('the device was already prepared; continuing') code = 0 if code: raise RuntimeError( 'cephadm exited with an error code: %d, stderr:%s' % (code, '\n'.join(err))) # check result out, err, code = CephadmServe(self.mgr)._run_cephadm( host, 'osd', 'ceph-volume', [ '--', 'lvm', 'list', '--format', 'json', ]) before_osd_uuid_map = self.mgr.get_osd_uuid_map(only_up=True) try: osds_elems = json.loads('\n'.join(out)) except ValueError: logger.exception('Cannot decode JSON: \'%s\'' % '\n'.join(out)) osds_elems = {} fsid = self.mgr._cluster_fsid osd_uuid_map = self.mgr.get_osd_uuid_map() created = [] for osd_id, osds in osds_elems.items(): for osd in osds: if osd['tags']['ceph.cluster_fsid'] != fsid: logger.debug('mismatched fsid, skipping %s' % osd) continue if osd_id in before_osd_uuid_map and osd_id not in replace_osd_ids: # if it exists but is part of the replacement operation, don't skip continue if osd_id not in osd_uuid_map: logger.debug( 'osd id {} does not exist in cluster'.format(osd_id)) continue if osd_uuid_map.get(osd_id) != osd['tags']['ceph.osd_fsid']: logger.debug('mismatched osd uuid (cluster has %s, osd ' 'has %s)' % (osd_uuid_map.get(osd_id), osd['tags']['ceph.osd_fsid'])) continue created.append(osd_id) daemon_spec: CephadmDaemonDeploySpec = CephadmDaemonDeploySpec( service_name=drive_group.service_name(), daemon_id=osd_id, host=host, daemon_type='osd', ) daemon_spec.final_config, daemon_spec.deps = self.generate_config( daemon_spec) CephadmServe(self.mgr)._create_daemon( daemon_spec, osd_uuid_map=osd_uuid_map) if created: self.mgr.cache.invalidate_host_devices(host) return "Created osd(s) %s on host '%s'" % (','.join(created), host) else: return "Created no osd(s) on host %s; already created?" % host