// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #include "svc_zone.h" #include "svc_rados.h" #include "svc_sys_obj.h" #include "svc_sync_modules.h" #include "rgw_zone.h" #include "rgw_rest_conn.h" #include "rgw_bucket_sync.h" #include "common/errno.h" #include "include/random.h" #define dout_subsys ceph_subsys_rgw using namespace std; using namespace rgw_zone_defaults; RGWSI_Zone::RGWSI_Zone(CephContext *cct) : RGWServiceInstance(cct) { } void RGWSI_Zone::init(RGWSI_SysObj *_sysobj_svc, RGWSI_RADOS * _rados_svc, RGWSI_SyncModules * _sync_modules_svc, RGWSI_Bucket_Sync *_bucket_sync_svc) { sysobj_svc = _sysobj_svc; rados_svc = _rados_svc; sync_modules_svc = _sync_modules_svc; bucket_sync_svc = _bucket_sync_svc; realm = new RGWRealm(); zonegroup = new RGWZoneGroup(); zone_public_config = new RGWZone(); zone_params = new RGWZoneParams(); current_period = new RGWPeriod(); } RGWSI_Zone::~RGWSI_Zone() { delete realm; delete zonegroup; delete zone_public_config; delete zone_params; delete current_period; } std::shared_ptr RGWSI_Zone::get_sync_policy_handler(std::optional zone) const { if (!zone || *zone == zone_id()) { return sync_policy_handler; } auto iter = sync_policy_handlers.find(*zone); if (iter == sync_policy_handlers.end()) { return std::shared_ptr(); } return iter->second; } bool RGWSI_Zone::zone_syncs_from(const RGWZone& target_zone, const RGWZone& source_zone) const { return target_zone.syncs_from(source_zone.name) && sync_modules_svc->get_manager()->supports_data_export(source_zone.tier_type); } bool RGWSI_Zone::zone_syncs_from(const RGWZone& source_zone) const { auto target_zone = get_zone(); bool found = false; for (auto s : data_sync_source_zones) { if (s->id == source_zone.id) { found = true; break; } } return found && target_zone.syncs_from(source_zone.name) && sync_modules_svc->get_manager()->supports_data_export(source_zone.tier_type); } int RGWSI_Zone::search_realm_with_zone(const DoutPrefixProvider *dpp, const rgw_zone_id& zid, RGWRealm *prealm, RGWPeriod *pperiod, RGWZoneGroup *pzonegroup, bool *pfound, optional_yield y) { auto& found = *pfound; found = false; list realms; int r = list_realms(dpp, realms); if (r < 0) { ldpp_dout(dpp, 0) << "ERROR: failed to list realms: r=" << r << dendl; return r; } for (auto& realm_name : realms) { string realm_id; RGWRealm realm(realm_id, realm_name); r = realm.init(dpp, cct, sysobj_svc, y); if (r < 0) { ldpp_dout(dpp, 0) << "WARNING: can't open realm " << realm_name << ": " << cpp_strerror(-r) << " ... skipping" << dendl; continue; } r = realm.find_zone(dpp, zid, pperiod, pzonegroup, &found, y); if (r < 0) { ldpp_dout(dpp, 20) << __func__ << "(): ERROR: realm.find_zone() returned r=" << r<< dendl; return r; } if (found) { *prealm = realm; ldpp_dout(dpp, 20) << __func__ << "(): found realm_id=" << realm_id << " realm_name=" << realm_name << dendl; return 0; } } return 0; } int RGWSI_Zone::do_start(optional_yield y, const DoutPrefixProvider *dpp) { int ret = sysobj_svc->start(y, dpp); if (ret < 0) { return ret; } assert(sysobj_svc->is_started()); /* if not then there's ordering issue */ ret = rados_svc->start(y, dpp); if (ret < 0) { return ret; } ret = realm->init(dpp, cct, sysobj_svc, y); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "failed reading realm info: ret "<< ret << " " << cpp_strerror(-ret) << dendl; return ret; } ldpp_dout(dpp, 20) << "realm " << realm->get_name() << " " << realm->get_id() << dendl; ret = current_period->init(dpp, cct, sysobj_svc, realm->get_id(), y, realm->get_name()); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "failed reading current period info: " << " " << cpp_strerror(-ret) << dendl; return ret; } ret = zone_params->init(dpp, cct, sysobj_svc, y); bool found_zone = (ret == 0); if (ret < 0 && ret != -ENOENT) { lderr(cct) << "failed reading zone info: ret "<< ret << " " << cpp_strerror(-ret) << dendl; return ret; } cur_zone_id = rgw_zone_id(zone_params->get_id()); bool found_period_conf = false; /* try to find zone in period config (if we have one) */ if (found_zone && !current_period->get_id().empty()) { found_period_conf = current_period->find_zone(dpp, cur_zone_id, zonegroup, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: current_period->find_zone() returned ret=" << ret << dendl; return ret; } if (!found_period_conf) { ldpp_dout(dpp, 0) << "period (" << current_period->get_id() << " does not have zone " << cur_zone_id << " configured" << dendl; } } RGWRealm search_realm; if (found_zone && !found_period_conf) { ldpp_dout(dpp, 20) << "searching for the correct realm" << dendl; ret = search_realm_with_zone(dpp, cur_zone_id, realm, current_period, zonegroup, &found_period_conf, y); if (ret < 0) { ldpp_dout(dpp, 0) << "ERROR: search_realm_conf() failed: ret="<< ret << dendl; return ret; } } bool zg_initialized = found_period_conf; if (!zg_initialized) { /* couldn't find a proper period config, use local zonegroup */ ret = zonegroup->init(dpp, cct, sysobj_svc, y); zg_initialized = (ret == 0); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "failed reading zonegroup info: " << cpp_strerror(-ret) << dendl; return ret; } } auto& zonegroup_param = cct->_conf->rgw_zonegroup; bool init_from_period = found_period_conf; bool explicit_zg = !zonegroup_param.empty(); if (!zg_initialized && (!explicit_zg || zonegroup_param == default_zonegroup_name)) { /* we couldn't initialize any zonegroup, falling back to a non-multisite config with default zonegroup */ ret = create_default_zg(dpp, y); if (ret < 0) { return ret; } zg_initialized = true; } if (!zg_initialized) { ldpp_dout(dpp, 0) << "ERROR: could not find zonegroup (" << zonegroup_param << ")" << dendl; return -ENOENT; } /* we have zonegroup now */ if (explicit_zg && zonegroup->get_name() != zonegroup_param) { ldpp_dout(dpp, 0) << "ERROR: incorrect zonegroup: " << zonegroup_param << " (got: " << zonegroup_param << ", expected: " << zonegroup->get_name() << ")" << dendl; return -EINVAL; } auto& zone_param = cct->_conf->rgw_zone; bool explicit_zone = !zone_param.empty(); if (!found_zone) { if ((!explicit_zone || zone_param == default_zone_name) && zonegroup->get_name() == default_zonegroup_name) { ret = init_default_zone(dpp, y); if (ret < 0 && ret != -ENOENT) { return ret; } cur_zone_id = zone_params->get_id(); } else { ldpp_dout(dpp, 0) << "ERROR: could not find zone (" << zone_param << ")" << dendl; return -ENOENT; } } /* we have zone now */ auto zone_iter = zonegroup->zones.find(zone_params->get_id()); if (zone_iter == zonegroup->zones.end()) { /* shouldn't happen if relying on period config */ if (!init_from_period) { ldpp_dout(dpp, -1) << "Cannot find zone id=" << zone_params->get_id() << " (name=" << zone_params->get_name() << ")" << dendl; return -EINVAL; } ldpp_dout(dpp, 1) << "Cannot find zone id=" << zone_params->get_id() << " (name=" << zone_params->get_name() << "), switching to local zonegroup configuration" << dendl; init_from_period = false; zone_iter = zonegroup->zones.find(zone_params->get_id()); } if (zone_iter == zonegroup->zones.end()) { ldpp_dout(dpp, -1) << "Cannot find zone id=" << zone_params->get_id() << " (name=" << zone_params->get_name() << ")" << dendl; return -EINVAL; } *zone_public_config = zone_iter->second; ldout(cct, 20) << "zone " << zone_params->get_name() << " found" << dendl; ldpp_dout(dpp, 4) << "Realm: " << std::left << setw(20) << realm->get_name() << " (" << realm->get_id() << ")" << dendl; ldpp_dout(dpp, 4) << "ZoneGroup: " << std::left << setw(20) << zonegroup->get_name() << " (" << zonegroup->get_id() << ")" << dendl; ldpp_dout(dpp, 4) << "Zone: " << std::left << setw(20) << zone_params->get_name() << " (" << zone_params->get_id() << ")" << dendl; if (init_from_period) { ldpp_dout(dpp, 4) << "using period configuration: " << current_period->get_id() << ":" << current_period->get_epoch() << dendl; ret = init_zg_from_period(dpp, y); if (ret < 0) { return ret; } } else { ldout(cct, 10) << "cannot find current period zonegroup using local zonegroup configuration" << dendl; ret = init_zg_from_local(dpp, y); if (ret < 0) { return ret; } // read period_config into current_period auto& period_config = current_period->get_config(); ret = period_config.read(dpp, sysobj_svc, zonegroup->realm_id, y); if (ret < 0 && ret != -ENOENT) { ldout(cct, 0) << "ERROR: failed to read period config: " << cpp_strerror(ret) << dendl; return ret; } } zone_short_id = current_period->get_map().get_zone_short_id(zone_params->get_id()); for (auto ziter : zonegroup->zones) { auto zone_handler = std::make_shared(this, sync_modules_svc, bucket_sync_svc, ziter.second.id); ret = zone_handler->init(dpp, y); if (ret < 0) { ldpp_dout(dpp, -1) << "ERROR: could not initialize zone policy handler for zone=" << ziter.second.name << dendl; return ret; } sync_policy_handlers[ziter.second.id] = zone_handler; } sync_policy_handler = sync_policy_handlers[zone_id()]; /* we made sure earlier that zonegroup->zones has our zone */ set source_zones; set target_zones; sync_policy_handler->reflect(dpp, nullptr, nullptr, nullptr, nullptr, &source_zones, &target_zones, false); /* relaxed: also get all zones that we allow to sync to/from */ ret = sync_modules_svc->start(y, dpp); if (ret < 0) { return ret; } auto sync_modules = sync_modules_svc->get_manager(); RGWSyncModuleRef sm; if (!sync_modules->get_module(zone_public_config->tier_type, &sm)) { ldpp_dout(dpp, -1) << "ERROR: tier type not found: " << zone_public_config->tier_type << dendl; return -EINVAL; } writeable_zone = sm->supports_writes(); exports_data = sm->supports_data_export(); /* first build all zones index */ for (auto ziter : zonegroup->zones) { const rgw_zone_id& id = ziter.first; RGWZone& z = ziter.second; zone_id_by_name[z.name] = id; zone_by_id[id] = z; } if (zone_by_id.find(zone_id()) == zone_by_id.end()) { ldpp_dout(dpp, 0) << "WARNING: could not find zone config in zonegroup for local zone (" << zone_id() << "), will use defaults" << dendl; } for (const auto& ziter : zonegroup->zones) { const rgw_zone_id& id = ziter.first; const RGWZone& z = ziter.second; if (id == zone_id()) { continue; } if (z.endpoints.empty()) { ldpp_dout(dpp, 0) << "WARNING: can't generate connection for zone " << z.id << " id " << z.name << ": no endpoints defined" << dendl; continue; } ldpp_dout(dpp, 20) << "generating connection object for zone " << z.name << " id " << z.id << dendl; RGWRESTConn *conn = new RGWRESTConn(cct, z.id, z.endpoints, zone_params->system_key, zonegroup->get_id(), zonegroup->api_name); zone_conn_map[id] = conn; bool zone_is_source = source_zones.find(z.id) != source_zones.end(); bool zone_is_target = target_zones.find(z.id) != target_zones.end(); if (zone_is_source || zone_is_target) { if (zone_is_source && sync_modules->supports_data_export(z.tier_type)) { data_sync_source_zones.push_back(&z); } if (zone_is_target) { zone_data_notify_to_map[id] = conn; } } else { ldpp_dout(dpp, 20) << "NOTICE: not syncing to/from zone " << z.name << " id " << z.id << dendl; } } ldpp_dout(dpp, 20) << "started zone id=" << zone_params->get_id() << " (name=" << zone_params->get_name() << ") with tier type = " << zone_public_config->tier_type << dendl; return 0; } void RGWSI_Zone::shutdown() { delete rest_master_conn; for (auto& item : zone_conn_map) { auto conn = item.second; delete conn; } for (auto& item : zonegroup_conn_map) { auto conn = item.second; delete conn; } } int RGWSI_Zone::list_regions(const DoutPrefixProvider *dpp, list& regions) { RGWZoneGroup zonegroup; RGWSI_SysObj::Pool syspool = sysobj_svc->get_pool(zonegroup.get_pool(cct)); return syspool.list_prefixed_objs(dpp, region_info_oid_prefix, ®ions); } int RGWSI_Zone::list_zonegroups(const DoutPrefixProvider *dpp, list& zonegroups) { RGWZoneGroup zonegroup; RGWSI_SysObj::Pool syspool = sysobj_svc->get_pool(zonegroup.get_pool(cct)); return syspool.list_prefixed_objs(dpp, zonegroup_names_oid_prefix, &zonegroups); } int RGWSI_Zone::list_zones(const DoutPrefixProvider *dpp, list& zones) { RGWZoneParams zoneparams; RGWSI_SysObj::Pool syspool = sysobj_svc->get_pool(zoneparams.get_pool(cct)); return syspool.list_prefixed_objs(dpp, zone_names_oid_prefix, &zones); } int RGWSI_Zone::list_realms(const DoutPrefixProvider *dpp, list& realms) { RGWRealm realm(cct, sysobj_svc); RGWSI_SysObj::Pool syspool = sysobj_svc->get_pool(realm.get_pool(cct)); return syspool.list_prefixed_objs(dpp, realm_names_oid_prefix, &realms); } int RGWSI_Zone::list_periods(const DoutPrefixProvider *dpp, list& periods) { RGWPeriod period; list raw_periods; RGWSI_SysObj::Pool syspool = sysobj_svc->get_pool(period.get_pool(cct)); int ret = syspool.list_prefixed_objs(dpp, period.get_info_oid_prefix(), &raw_periods); if (ret < 0) { return ret; } for (const auto& oid : raw_periods) { size_t pos = oid.find("."); if (pos != std::string::npos) { periods.push_back(oid.substr(0, pos)); } else { periods.push_back(oid); } } periods.sort(); // unique() only detects duplicates if they're adjacent periods.unique(); return 0; } int RGWSI_Zone::list_periods(const DoutPrefixProvider *dpp, const string& current_period, list& periods, optional_yield y) { int ret = 0; string period_id = current_period; while(!period_id.empty()) { RGWPeriod period(period_id); ret = period.init(dpp, cct, sysobj_svc, y); if (ret < 0) { return ret; } periods.push_back(period.get_id()); period_id = period.get_predecessor(); } return ret; } /** * Add new connection to connections map * @param zonegroup_conn_map map which new connection will be added to * @param zonegroup zonegroup which new connection will connect to * @param new_connection pointer to new connection instance */ static void add_new_connection_to_map(map &zonegroup_conn_map, const RGWZoneGroup &zonegroup, RGWRESTConn *new_connection) { // Delete if connection is already exists map::iterator iterZoneGroup = zonegroup_conn_map.find(zonegroup.get_id()); if (iterZoneGroup != zonegroup_conn_map.end()) { delete iterZoneGroup->second; } // Add new connection to connections map zonegroup_conn_map[zonegroup.get_id()] = new_connection; } int RGWSI_Zone::init_zg_from_period(const DoutPrefixProvider *dpp, optional_yield y) { ldout(cct, 20) << "period zonegroup name " << zonegroup->get_name() << dendl; map::const_iterator iter = current_period->get_map().zonegroups.find(zonegroup->get_id()); if (iter != current_period->get_map().zonegroups.end()) { ldpp_dout(dpp, 20) << "using current period zonegroup " << zonegroup->get_name() << dendl; *zonegroup = iter->second; int ret = zonegroup->init(dpp, cct, sysobj_svc, y, false); if (ret < 0) { ldpp_dout(dpp, 0) << "failed init zonegroup: " << " " << cpp_strerror(-ret) << dendl; return ret; } } for (iter = current_period->get_map().zonegroups.begin(); iter != current_period->get_map().zonegroups.end(); ++iter){ const RGWZoneGroup& zg = iter->second; // use endpoints from the zonegroup's master zone auto master = zg.zones.find(zg.master_zone); if (master == zg.zones.end()) { // Check for empty zonegroup which can happen if zone was deleted before removal if (zg.zones.size() == 0) continue; // fix missing master zone for a single zone zonegroup if (zg.master_zone.empty() && zg.zones.size() == 1) { master = zg.zones.begin(); ldpp_dout(dpp, 0) << "zonegroup " << zg.get_name() << " missing master_zone, setting zone " << master->second.name << " id:" << master->second.id << " as master" << dendl; if (zonegroup->get_id() == zg.get_id()) { zonegroup->master_zone = master->second.id; int ret = zonegroup->update(dpp, y); if (ret < 0) { ldpp_dout(dpp, 0) << "error updating zonegroup : " << cpp_strerror(-ret) << dendl; return ret; } } else { RGWZoneGroup fixed_zg(zg.get_id(),zg.get_name()); int ret = fixed_zg.init(dpp, cct, sysobj_svc, y); if (ret < 0) { ldpp_dout(dpp, 0) << "error initializing zonegroup : " << cpp_strerror(-ret) << dendl; return ret; } fixed_zg.master_zone = master->second.id; ret = fixed_zg.update(dpp, y); if (ret < 0) { ldpp_dout(dpp, 0) << "error initializing zonegroup : " << cpp_strerror(-ret) << dendl; return ret; } } } else { ldpp_dout(dpp, 0) << "zonegroup " << zg.get_name() << " missing zone for master_zone=" << zg.master_zone << dendl; return -EINVAL; } } const auto& endpoints = master->second.endpoints; add_new_connection_to_map(zonegroup_conn_map, zg, new RGWRESTConn(cct, zg.get_id(), endpoints, zone_params->system_key, zonegroup->get_id(), zg.api_name)); if (!current_period->get_master_zonegroup().empty() && zg.get_id() == current_period->get_master_zonegroup()) { rest_master_conn = new RGWRESTConn(cct, zg.get_id(), endpoints, zone_params->system_key, zonegroup->get_id(), zg.api_name); } } return 0; } int RGWSI_Zone::create_default_zg(const DoutPrefixProvider *dpp, optional_yield y) { ldout(cct, 10) << "Creating default zonegroup " << dendl; int ret = zonegroup->create_default(dpp, y); if (ret < 0) { ldpp_dout(dpp, 0) << "failure in zonegroup create_default: ret "<< ret << " " << cpp_strerror(-ret) << dendl; return ret; } ret = zonegroup->init(dpp, cct, sysobj_svc, y); if (ret < 0) { ldout(cct, 0) << "failure in zonegroup create_default: ret "<< ret << " " << cpp_strerror(-ret) << dendl; return ret; } return 0; } int RGWSI_Zone::init_default_zone(const DoutPrefixProvider *dpp, optional_yield y) { ldpp_dout(dpp, 10) << " Using default name "<< default_zone_name << dendl; zone_params->set_name(default_zone_name); int ret = zone_params->init(dpp, cct, sysobj_svc, y); if (ret < 0 && ret != -ENOENT) { ldpp_dout(dpp, 0) << "failed reading zone params info: " << " " << cpp_strerror(-ret) << dendl; return ret; } return 0; } int RGWSI_Zone::init_zg_from_local(const DoutPrefixProvider *dpp, optional_yield y) { ldpp_dout(dpp, 20) << "zonegroup " << zonegroup->get_name() << dendl; if (zonegroup->is_master_zonegroup()) { // use endpoints from the zonegroup's master zone auto master = zonegroup->zones.find(zonegroup->master_zone); if (master == zonegroup->zones.end()) { // fix missing master zone for a single zone zonegroup if (zonegroup->master_zone.empty() && zonegroup->zones.size() == 1) { master = zonegroup->zones.begin(); ldpp_dout(dpp, 0) << "zonegroup " << zonegroup->get_name() << " missing master_zone, setting zone " << master->second.name << " id:" << master->second.id << " as master" << dendl; zonegroup->master_zone = master->second.id; int ret = zonegroup->update(dpp, y); if (ret < 0) { ldpp_dout(dpp, 0) << "error initializing zonegroup : " << cpp_strerror(-ret) << dendl; return ret; } } else { ldpp_dout(dpp, 0) << "zonegroup " << zonegroup->get_name() << " missing zone for " "master_zone=" << zonegroup->master_zone << dendl; return -EINVAL; } } const auto& endpoints = master->second.endpoints; rest_master_conn = new RGWRESTConn(cct, zonegroup->get_id(), endpoints, zone_params->system_key, zonegroup->get_id(), zonegroup->api_name); } return 0; } const RGWZoneParams& RGWSI_Zone::get_zone_params() const { return *zone_params; } const RGWZone& RGWSI_Zone::get_zone() const { return *zone_public_config; } const RGWZoneGroup& RGWSI_Zone::get_zonegroup() const { return *zonegroup; } int RGWSI_Zone::get_zonegroup(const string& id, RGWZoneGroup& zg) const { int ret = 0; if (id == zonegroup->get_id()) { zg = *zonegroup; } else if (!current_period->get_id().empty()) { ret = current_period->get_zonegroup(zg, id); } return ret; } const RGWRealm& RGWSI_Zone::get_realm() const { return *realm; } const RGWPeriod& RGWSI_Zone::get_current_period() const { return *current_period; } const string& RGWSI_Zone::get_current_period_id() const { return current_period->get_id(); } bool RGWSI_Zone::has_zonegroup_api(const std::string& api) const { if (!current_period->get_id().empty()) { const auto& zonegroups_by_api = current_period->get_map().zonegroups_by_api; if (zonegroups_by_api.find(api) != zonegroups_by_api.end()) return true; } else if (zonegroup->api_name == api) { return true; } return false; } bool RGWSI_Zone::zone_is_writeable() { return writeable_zone && !get_zone().is_read_only(); } uint32_t RGWSI_Zone::get_zone_short_id() const { return zone_short_id; } const string& RGWSI_Zone::zone_name() const { return get_zone_params().get_name(); } RGWZone* RGWSI_Zone::find_zone(const rgw_zone_id& id) { auto iter = zone_by_id.find(id); if (iter == zone_by_id.end()) { return nullptr; } return &(iter->second); } RGWRESTConn *RGWSI_Zone::get_zone_conn(const rgw_zone_id& zone_id) { auto citer = zone_conn_map.find(zone_id.id); if (citer == zone_conn_map.end()) { return NULL; } return citer->second; } RGWRESTConn *RGWSI_Zone::get_zone_conn_by_name(const string& name) { auto i = zone_id_by_name.find(name); if (i == zone_id_by_name.end()) { return NULL; } return get_zone_conn(i->second); } bool RGWSI_Zone::find_zone_id_by_name(const string& name, rgw_zone_id *id) { auto i = zone_id_by_name.find(name); if (i == zone_id_by_name.end()) { return false; } *id = i->second; return true; } bool RGWSI_Zone::need_to_sync() const { return !(zonegroup->master_zone.empty() || !rest_master_conn || current_period->get_id().empty()); } bool RGWSI_Zone::need_to_log_data() const { return (zone_public_config->log_data && sync_module_exports_data()); } bool RGWSI_Zone::is_meta_master() const { if (!zonegroup->is_master_zonegroup()) { return false; } return (zonegroup->master_zone == zone_public_config->id); } bool RGWSI_Zone::need_to_log_metadata() const { return is_meta_master() && (zonegroup->zones.size() > 1 || current_period->is_multi_zonegroups_with_zones()); } bool RGWSI_Zone::can_reshard() const { if (current_period->get_id().empty()) { return true; // no realm } if (zonegroup->zones.size() == 1 && current_period->is_single_zonegroup()) { return true; // single zone/zonegroup } // 'resharding' feature enabled in zonegroup return zonegroup->supports(rgw::zone_features::resharding); } /** * Check to see if the bucket metadata could be synced * bucket: the bucket to check * Returns false is the bucket is not synced */ bool RGWSI_Zone::is_syncing_bucket_meta(const rgw_bucket& bucket) { /* no current period */ if (current_period->get_id().empty()) { return false; } /* zonegroup is not master zonegroup */ if (!zonegroup->is_master_zonegroup()) { return false; } /* single zonegroup and a single zone */ if (current_period->is_single_zonegroup() && zonegroup->zones.size() == 1) { return false; } /* zone is not master */ if (zonegroup->master_zone != zone_public_config->id) { return false; } return true; } int RGWSI_Zone::select_new_bucket_location(const DoutPrefixProvider *dpp, const RGWUserInfo& user_info, const string& zonegroup_id, const rgw_placement_rule& request_rule, rgw_placement_rule *pselected_rule_name, RGWZonePlacementInfo *rule_info, optional_yield y) { /* first check that zonegroup exists within current period. */ RGWZoneGroup zonegroup; int ret = get_zonegroup(zonegroup_id, zonegroup); if (ret < 0) { ldpp_dout(dpp, 0) << "could not find zonegroup " << zonegroup_id << " in current period" << dendl; return ret; } const rgw_placement_rule *used_rule; /* find placement rule. Hierarchy: request rule > user default rule > zonegroup default rule */ std::map::const_iterator titer; if (!request_rule.name.empty()) { used_rule = &request_rule; titer = zonegroup.placement_targets.find(request_rule.name); if (titer == zonegroup.placement_targets.end()) { ldpp_dout(dpp, 0) << "could not find requested placement id " << request_rule << " within zonegroup " << dendl; return -ERR_INVALID_LOCATION_CONSTRAINT; } } else if (!user_info.default_placement.name.empty()) { used_rule = &user_info.default_placement; titer = zonegroup.placement_targets.find(user_info.default_placement.name); if (titer == zonegroup.placement_targets.end()) { ldpp_dout(dpp, 0) << "could not find user default placement id " << user_info.default_placement << " within zonegroup " << dendl; return -ERR_INVALID_LOCATION_CONSTRAINT; } } else { if (zonegroup.default_placement.name.empty()) { // zonegroup default rule as fallback, it should not be empty. ldpp_dout(dpp, 0) << "misconfiguration, zonegroup default placement id should not be empty." << dendl; return -ERR_ZONEGROUP_DEFAULT_PLACEMENT_MISCONFIGURATION; } else { used_rule = &zonegroup.default_placement; titer = zonegroup.placement_targets.find(zonegroup.default_placement.name); if (titer == zonegroup.placement_targets.end()) { ldpp_dout(dpp, 0) << "could not find zonegroup default placement id " << zonegroup.default_placement << " within zonegroup " << dendl; return -ERR_INVALID_LOCATION_CONSTRAINT; } } } /* now check tag for the rule, whether user is permitted to use rule */ const auto& target_rule = titer->second; if (!target_rule.user_permitted(user_info.placement_tags)) { ldpp_dout(dpp, 0) << "user not permitted to use placement rule " << titer->first << dendl; return -EPERM; } const string *storage_class = &request_rule.storage_class; if (storage_class->empty()) { storage_class = &used_rule->storage_class; } rgw_placement_rule rule(titer->first, *storage_class); if (pselected_rule_name) { *pselected_rule_name = rule; } return select_bucket_location_by_rule(dpp, rule, rule_info, y); } int RGWSI_Zone::select_bucket_location_by_rule(const DoutPrefixProvider *dpp, const rgw_placement_rule& location_rule, RGWZonePlacementInfo *rule_info, optional_yield y) { if (location_rule.name.empty()) { /* we can only reach here if we're trying to set a bucket location from a bucket * created on a different zone, using a legacy / default pool configuration */ if (rule_info) { return select_legacy_bucket_placement(dpp, rule_info, y); } return 0; } /* * make sure that zone has this rule configured. We're * checking it for the local zone, because that's where this bucket object is going to * reside. */ auto piter = zone_params->placement_pools.find(location_rule.name); if (piter == zone_params->placement_pools.end()) { /* couldn't find, means we cannot really place data for this bucket in this zone */ ldpp_dout(dpp, 0) << "ERROR: This zone does not contain placement rule " << location_rule << " present in the zonegroup!" << dendl; return -EINVAL; } auto storage_class = location_rule.get_storage_class(); if (!piter->second.storage_class_exists(storage_class)) { ldpp_dout(dpp, 5) << "requested storage class does not exist: " << storage_class << dendl; return -EINVAL; } RGWZonePlacementInfo& placement_info = piter->second; if (rule_info) { *rule_info = placement_info; } return 0; } int RGWSI_Zone::select_bucket_placement(const DoutPrefixProvider *dpp, const RGWUserInfo& user_info, const string& zonegroup_id, const rgw_placement_rule& placement_rule, rgw_placement_rule *pselected_rule, RGWZonePlacementInfo *rule_info, optional_yield y) { if (!zone_params->placement_pools.empty()) { return select_new_bucket_location(dpp, user_info, zonegroup_id, placement_rule, pselected_rule, rule_info, y); } if (pselected_rule) { pselected_rule->clear(); } if (rule_info) { return select_legacy_bucket_placement(dpp, rule_info, y); } return 0; } int RGWSI_Zone::select_legacy_bucket_placement(const DoutPrefixProvider *dpp, RGWZonePlacementInfo *rule_info, optional_yield y) { bufferlist map_bl; map m; string pool_name; bool write_map = false; rgw_raw_obj obj(zone_params->domain_root, avail_pools); auto sysobj = sysobj_svc->get_obj(obj); int ret = sysobj.rop().read(dpp, &map_bl, y); if (ret < 0) { goto read_omap; } try { auto iter = map_bl.cbegin(); decode(m, iter); } catch (buffer::error& err) { ldpp_dout(dpp, 0) << "ERROR: couldn't decode avail_pools" << dendl; } read_omap: if (m.empty()) { ret = sysobj.omap().get_all(dpp, &m, y); write_map = true; } if (ret < 0 || m.empty()) { vector pools; string s = string("default.") + default_storage_pool_suffix; pools.push_back(rgw_pool(s)); vector retcodes; bufferlist bl; ret = rados_svc->pool().create(dpp, pools, &retcodes); if (ret < 0) return ret; ret = sysobj.omap().set(dpp, s, bl, y); if (ret < 0) return ret; m[s] = bl; } if (write_map) { bufferlist new_bl; encode(m, new_bl); ret = sysobj.wop().write(dpp, new_bl, y); if (ret < 0) { ldpp_dout(dpp, 0) << "WARNING: could not save avail pools map info ret=" << ret << dendl; } } auto miter = m.begin(); if (m.size() > 1) { // choose a pool at random auto r = ceph::util::generate_random_number(0, m.size() - 1); std::advance(miter, r); } pool_name = miter->first; rgw_pool pool = pool_name; rule_info->storage_classes.set_storage_class(RGW_STORAGE_CLASS_STANDARD, &pool, nullptr); rule_info->data_extra_pool = pool_name; rule_info->index_pool = pool_name; rule_info->index_type = rgw::BucketIndexType::Normal; return 0; } int RGWSI_Zone::update_placement_map(const DoutPrefixProvider *dpp, optional_yield y) { bufferlist header; map m; rgw_raw_obj obj(zone_params->domain_root, avail_pools); auto sysobj = sysobj_svc->get_obj(obj); int ret = sysobj.omap().get_all(dpp, &m, y); if (ret < 0) return ret; bufferlist new_bl; encode(m, new_bl); ret = sysobj.wop().write(dpp, new_bl, y); if (ret < 0) { ldpp_dout(dpp, 0) << "WARNING: could not save avail pools map info ret=" << ret << dendl; } return ret; } int RGWSI_Zone::add_bucket_placement(const DoutPrefixProvider *dpp, const rgw_pool& new_pool, optional_yield y) { int ret = rados_svc->pool(new_pool).lookup(); if (ret < 0) { // DNE, or something return ret; } rgw_raw_obj obj(zone_params->domain_root, avail_pools); auto sysobj = sysobj_svc->get_obj(obj); bufferlist empty_bl; ret = sysobj.omap().set(dpp, new_pool.to_str(), empty_bl, y); // don't care about return value update_placement_map(dpp, y); return ret; } int RGWSI_Zone::remove_bucket_placement(const DoutPrefixProvider *dpp, const rgw_pool& old_pool, optional_yield y) { rgw_raw_obj obj(zone_params->domain_root, avail_pools); auto sysobj = sysobj_svc->get_obj(obj); int ret = sysobj.omap().del(dpp, old_pool.to_str(), y); // don't care about return value update_placement_map(dpp, y); return ret; } int RGWSI_Zone::list_placement_set(const DoutPrefixProvider *dpp, set& names, optional_yield y) { bufferlist header; map m; rgw_raw_obj obj(zone_params->domain_root, avail_pools); auto sysobj = sysobj_svc->get_obj(obj); int ret = sysobj.omap().get_all(dpp, &m, y); if (ret < 0) return ret; names.clear(); map::iterator miter; for (miter = m.begin(); miter != m.end(); ++miter) { names.insert(rgw_pool(miter->first)); } return names.size(); } bool RGWSI_Zone::get_redirect_zone_endpoint(string *endpoint) { if (zone_public_config->redirect_zone.empty()) { return false; } auto iter = zone_conn_map.find(zone_public_config->redirect_zone); if (iter == zone_conn_map.end()) { ldout(cct, 0) << "ERROR: cannot find entry for redirect zone: " << zone_public_config->redirect_zone << dendl; return false; } RGWRESTConn *conn = iter->second; int ret = conn->get_url(*endpoint); if (ret < 0) { ldout(cct, 0) << "ERROR: redirect zone, conn->get_endpoint() returned ret=" << ret << dendl; return false; } return true; }