diff options
| author | Jafar Al-Gharaibeh <jafar@atcorp.com> | 2023-06-25 10:22:34 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-25 10:22:34 -0500 | 
| commit | 64ef7364c6c4ef8a0db7f47fdd9de4e451763702 (patch) | |
| tree | 13878218d483ef79438e03ce6375969878b6ea02 /lib/vty.c | |
| parent | 0ea4297e3c3e55d3894820f9359215b6957fa213 (diff) | |
| parent | 6dccdb9b0d1b3bbf04196f712ebccf9a8f39e47e (diff) | |
Merge pull request #13846 from LabNConsulting/chopps/backport-13795frr-9.0-rc
backport of #13795
Diffstat (limited to 'lib/vty.c')
| -rw-r--r-- | lib/vty.c | 333 | 
1 files changed, 192 insertions, 141 deletions
@@ -70,7 +70,6 @@ struct nb_config *vty_mgmt_candidate_config;  static struct mgmt_fe_client *mgmt_fe_client;  static bool mgmt_fe_connected; -static bool mgmt_candidate_ds_wr_locked;  static uint64_t mgmt_client_id_next;  static uint64_t mgmt_last_req_id = UINT64_MAX; @@ -129,6 +128,35 @@ char const *const mgmt_daemons[] = {  };  uint mgmt_daemons_count = array_size(mgmt_daemons); + +static int vty_mgmt_lock_candidate_inline(struct vty *vty) +{ +	assert(!vty->mgmt_locked_candidate_ds); +	(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, true, true); +	return vty->mgmt_locked_candidate_ds ? 0 : -1; +} + +static int vty_mgmt_unlock_candidate_inline(struct vty *vty) +{ +	assert(vty->mgmt_locked_candidate_ds); +	(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false, true); +	return vty->mgmt_locked_candidate_ds ? -1 : 0; +} + +static int vty_mgmt_lock_running_inline(struct vty *vty) +{ +	assert(!vty->mgmt_locked_running_ds); +	(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, true, true); +	return vty->mgmt_locked_running_ds ? 0 : -1; +} + +static int vty_mgmt_unlock_running_inline(struct vty *vty) +{ +	assert(vty->mgmt_locked_running_ds); +	(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, false, true); +	return vty->mgmt_locked_running_ds ? -1 : 0; +} +  void vty_mgmt_resume_response(struct vty *vty, bool success)  {  	uint8_t header[4] = {0, 0, 0, 0}; @@ -1648,12 +1676,12 @@ struct vty *vty_new(void)  		if (!mgmt_client_id_next)  			mgmt_client_id_next++;  		new->mgmt_client_id = mgmt_client_id_next++; -		if (mgmt_fe_create_client_session( -			    mgmt_fe_client, new->mgmt_client_id, -			    (uintptr_t) new) != MGMTD_SUCCESS) -			zlog_err( -				"Failed to open a MGMTD Frontend session for VTY session %p!!", -				new); +		new->mgmt_session_id = 0; +		mgmt_fe_create_client_session( +			mgmt_fe_client, new->mgmt_client_id, (uintptr_t) new); +		/* we short-circuit create the session so it must be set now */ +		assertf(new->mgmt_session_id != 0, +			"Failed to create client session for VTY");  	}  	return new; @@ -2202,10 +2230,11 @@ bool mgmt_vty_read_configs(void)  	vty->node = CONFIG_NODE;  	vty->config = true;  	vty->pending_allowed = true; +  	vty->candidate_config = vty_shared_candidate_config; -	vty->mgmt_locked_candidate_ds = true; -	mgmt_candidate_ds_wr_locked = true; +	vty_mgmt_lock_candidate_inline(vty); +	vty_mgmt_lock_running_inline(vty);  	for (index = 0; index < array_size(mgmt_daemons); index++) {  		snprintf(path, sizeof(path), "%s/%s.conf", frr_sysconfdir, @@ -2250,10 +2279,15 @@ bool mgmt_vty_read_configs(void)  		fclose(confp);  	} -	vty->pending_allowed = false; +	/* Conditionally unlock as the config file may have "exit"d early which +	 * would then have unlocked things. +	 */ +	if (vty->mgmt_locked_running_ds) +		vty_mgmt_unlock_running_inline(vty); +	if (vty->mgmt_locked_candidate_ds) +		vty_mgmt_unlock_candidate_inline(vty); -	vty->mgmt_locked_candidate_ds = false; -	mgmt_candidate_ds_wr_locked = false; +	vty->pending_allowed = false;  	if (!count)  		vty_close(vty); @@ -2367,7 +2401,7 @@ static void vtysh_read(struct event *thread)  				 */  				if (vty->mgmt_req_pending_cmd) {  					MGMTD_FE_CLIENT_DBG( -						"postpone CLI cmd response pending mgmtd %s on vty session-id %" PRIu64, +						"postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,  						vty->mgmt_req_pending_cmd,  						vty->mgmt_session_id);  					return; @@ -2453,6 +2487,9 @@ void vty_close(struct vty *vty)  		MGMTD_FE_CLIENT_ERR(  			"vty closed, uncommitted config will be lost."); +	/* Drop out of configure / transaction if needed. */ +	vty_config_exit(vty); +  	if (mgmt_fe_client && vty->mgmt_session_id) {  		MGMTD_FE_CLIENT_DBG("closing vty session");  		mgmt_fe_destroy_client_session(mgmt_fe_client, @@ -2460,9 +2497,6 @@ void vty_close(struct vty *vty)  		vty->mgmt_session_id = 0;  	} -	/* Drop out of configure / transaction if needed. */ -	vty_config_exit(vty); -  	/* Cancel threads.*/  	EVENT_OFF(vty->t_read);  	EVENT_OFF(vty->t_write); @@ -2821,28 +2855,35 @@ bool vty_read_config(struct nb_config *config, const char *config_file,  	return true;  } -int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) +int vty_config_enter(struct vty *vty, bool private_config, bool exclusive, +		     bool file_lock)  { -	if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) { +	if (exclusive && !vty_mgmt_fe_enabled() && +	    nb_running_lock(NB_CLIENT_CLI, vty)) {  		vty_out(vty, "%% Configuration is locked by other client\n");  		return CMD_WARNING;  	} -	if (vty_mgmt_fe_enabled()) { -		if (!mgmt_candidate_ds_wr_locked) { -			if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, -						     true) != 0) { -				vty_out(vty, "Not able to lock candidate DS\n"); -				return CMD_WARNING; -			} -		} else { +	/* +	 * We only need to do a lock when reading a config file as we will be +	 * sending a batch of setcfg changes followed by a single commit +	 * message. For user interactive mode we are doing implicit commits +	 * those will obtain the lock (or not) when they try and commit. +	 */ +	if (file_lock && vty_mgmt_fe_enabled() && !private_config) { +		if (vty_mgmt_lock_candidate_inline(vty)) {  			vty_out(vty, -				"Candidate DS already locked by different session\n"); -			return CMD_WARNING; +				"%% Can't enter config; candidate datastore locked by another session\n"); +			return CMD_WARNING_CONFIG_FAILED;  		} - -		vty->mgmt_locked_candidate_ds = true; -		mgmt_candidate_ds_wr_locked = true; +		if (vty_mgmt_lock_running_inline(vty)) { +			vty_out(vty, +				"%% Can't enter config; running datastore locked by another session\n"); +			vty_mgmt_unlock_candidate_inline(vty); +			return CMD_WARNING_CONFIG_FAILED; +		} +		assert(vty->mgmt_locked_candidate_ds); +		assert(vty->mgmt_locked_running_ds);  	}  	vty->node = CONFIG_NODE; @@ -2855,23 +2896,24 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)  		vty->candidate_config_base = nb_config_dup(running_config);  		vty_out(vty,  			"Warning: uncommitted changes will be discarded on exit.\n\n"); -	} else { -		/* -		 * NOTE: On the MGMTD daemon we point the VTY candidate DS to -		 * the global MGMTD candidate DS. Else we point to the VTY -		 * Shared Candidate Config. -		 */ -		vty->candidate_config = vty_mgmt_candidate_config -						? vty_mgmt_candidate_config -						: vty_shared_candidate_config; -		if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) -			vty->candidate_config_base = -				nb_config_dup(running_config); +		return CMD_SUCCESS;  	} +	/* +	 * NOTE: On the MGMTD daemon we point the VTY candidate DS to +	 * the global MGMTD candidate DS. Else we point to the VTY +	 * Shared Candidate Config. +	 */ +	vty->candidate_config = vty_mgmt_candidate_config +					? vty_mgmt_candidate_config +					: vty_shared_candidate_config; +	if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) +		vty->candidate_config_base = nb_config_dup(running_config); +  	return CMD_SUCCESS;  } +  void vty_config_exit(struct vty *vty)  {  	enum node_type node = vty->node; @@ -2896,21 +2938,13 @@ int vty_config_node_exit(struct vty *vty)  {  	vty->xpath_index = 0; -	/* -	 * If we are not reading config file and we are mgmtd FE and we are -	 * locked then unlock. -	 */ -	if (vty->type != VTY_FILE && vty_mgmt_fe_enabled() && -	    mgmt_candidate_ds_wr_locked && vty->mgmt_locked_candidate_ds) { -		if (vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false) != -		    0) { -			vty_out(vty, "Not able to unlock candidate DS\n"); -			return CMD_WARNING; -		} +	/* TODO: could we check for un-commited changes here? */ -		vty->mgmt_locked_candidate_ds = false; -		mgmt_candidate_ds_wr_locked = false; -	} +	if (vty->mgmt_locked_running_ds) +		vty_mgmt_unlock_running_inline(vty); + +	if (vty->mgmt_locked_candidate_ds) +		vty_mgmt_unlock_candidate_inline(vty);  	/* Perform any pending commits. */  	(void)nb_cli_pending_commit_check(vty); @@ -3493,23 +3527,28 @@ static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,  	vty = (struct vty *)session_ctx; -	if (!success) { -		zlog_err("%socking for DS %u failed, Err: '%s'", -			 lock_ds ? "L" : "Unl", ds_id, errmsg_if_any); -		vty_out(vty, "ERROR: %socking for DS %u failed, Err: '%s'\n", -			lock_ds ? "L" : "Unl", ds_id, errmsg_if_any); -	} else { +	assert(ds_id == MGMTD_DS_CANDIDATE || ds_id == MGMTD_DS_RUNNING); +	if (!success) +		zlog_err("%socking for DS %u failed, Err: '%s' vty %p", +			 lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty); +	else {  		MGMTD_FE_CLIENT_DBG("%socked DS %u successfully",  				    lock_ds ? "L" : "Unl", ds_id); +		if (ds_id == MGMTD_DS_CANDIDATE) +			vty->mgmt_locked_candidate_ds = lock_ds; +		else +			vty->mgmt_locked_running_ds = lock_ds;  	} -	vty_mgmt_resume_response(vty, success); +	if (vty->mgmt_req_pending_cmd) +		vty_mgmt_resume_response(vty, success);  }  static void vty_mgmt_set_config_result_notified(  	struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,  	uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id, -	bool success, Mgmtd__DatastoreId ds_id, char *errmsg_if_any) +	bool success, Mgmtd__DatastoreId ds_id, bool implicit_commit, +	char *errmsg_if_any)  {  	struct vty *vty; @@ -3527,6 +3566,12 @@ static void vty_mgmt_set_config_result_notified(  				    client_id, req_id);  	} +	if (implicit_commit) { +		/* In this case the changes have been applied, we are done */ +		vty_mgmt_unlock_candidate_inline(vty); +		vty_mgmt_unlock_running_inline(vty); +	} +  	vty_mgmt_resume_response(vty, success);  } @@ -3632,23 +3677,24 @@ bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty)  }  int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, -			     bool lock) +			     bool lock, bool scok)  { -	if (mgmt_fe_client && vty->mgmt_session_id) { -		vty->mgmt_req_id++; -		if (mgmt_fe_send_lockds_req(mgmt_fe_client, -					    vty->mgmt_session_id, -					    vty->mgmt_req_id, ds_id, lock)) { -			zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64, -				 lock ? "" : "UN", vty->mgmt_req_id); -			vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n", -				lock ? "" : "UN"); -			return -1; -		} +	assert(mgmt_fe_client); +	assert(vty->mgmt_session_id); -		vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ"; +	vty->mgmt_req_id++; +	if (mgmt_fe_send_lockds_req(mgmt_fe_client, vty->mgmt_session_id, +				    vty->mgmt_req_id, ds_id, lock, scok)) { +		zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64, +			 lock ? "" : "UN", vty->mgmt_req_id); +		vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n", +			lock ? "" : "UN"); +		return -1;  	} +	if (!scok) +		vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ"; +  	return 0;  } @@ -3659,7 +3705,6 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)  	Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];  	Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};  	size_t indx; -	int cnt;  	if (vty->type == VTY_FILE) {  		/* @@ -3667,86 +3712,92 @@ int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)  		 * changes until we are done reading the file and have modified  		 * the local candidate DS.  		 */ -		assert(vty->mgmt_locked_candidate_ds);  		/* no-one else should be sending data right now */  		assert(!vty->mgmt_num_pending_setcfg);  		return 0;  	} +	/* If we are FE client and we have a vty then we have a session */ +	assert(mgmt_fe_client && vty->mgmt_client_id && vty->mgmt_session_id); -	if (mgmt_fe_client && vty->mgmt_client_id && !vty->mgmt_session_id) { -		/* -		 * We are connected to mgmtd but we do not yet have an -		 * established session. this means we need to send any changes -		 * made during this "down-time" to all backend clients when this -		 * FE client finishes coming up. -		 */ -		MGMTD_FE_CLIENT_DBG("skipping as no session exists"); +	if (!vty->num_cfg_changes)  		return 0; + +	/* grab the candidate and running lock prior to sending implicit commit +	 * command +	 */ +	if (implicit_commit) { +		if (vty_mgmt_lock_candidate_inline(vty)) { +			vty_out(vty, +				"%% command failed, could not lock candidate DS\n"); +			return -1; +		} else if (vty_mgmt_lock_running_inline(vty)) { +			vty_out(vty, +				"%% command failed, could not lock running DS\n"); +			return -1; +		}  	} -	if (mgmt_fe_client && vty->mgmt_session_id) { -		cnt = 0; -		for (indx = 0; indx < vty->num_cfg_changes; indx++) { -			mgmt_yang_data_init(&cfg_data[cnt]); - -			if (vty->cfg_changes[indx].value) { -				mgmt_yang_data_value_init(&value[cnt]); -				value[cnt].encoded_str_val = -					(char *)vty->cfg_changes[indx].value; -				value[cnt].value_case = -					MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; -				cfg_data[cnt].value = &value[cnt]; -			} +	for (indx = 0; indx < vty->num_cfg_changes; indx++) { +		mgmt_yang_data_init(&cfg_data[indx]); -			cfg_data[cnt].xpath = vty->cfg_changes[indx].xpath; +		if (vty->cfg_changes[indx].value) { +			mgmt_yang_data_value_init(&value[indx]); +			value[indx].encoded_str_val = +				(char *)vty->cfg_changes[indx].value; +			value[indx].value_case = +				MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; +			cfg_data[indx].value = &value[indx]; +		} -			mgmt_yang_cfg_data_req_init(&cfg_req[cnt]); -			cfg_req[cnt].data = &cfg_data[cnt]; -			switch (vty->cfg_changes[indx].operation) { -			case NB_OP_DESTROY: -				cfg_req[cnt].req_type = -					MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; -				break; +		cfg_data[indx].xpath = vty->cfg_changes[indx].xpath; -			case NB_OP_CREATE: -			case NB_OP_MODIFY: -			case NB_OP_MOVE: -			case NB_OP_PRE_VALIDATE: -			case NB_OP_APPLY_FINISH: -				cfg_req[cnt].req_type = -					MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; -				break; -			case NB_OP_GET_ELEM: -			case NB_OP_GET_NEXT: -			case NB_OP_GET_KEYS: -			case NB_OP_LOOKUP_ENTRY: -			case NB_OP_RPC: -				assert(!"Invalid type of operation"); -				break; -			default: -				assert(!"non-enum value, invalid"); -			} +		mgmt_yang_cfg_data_req_init(&cfg_req[indx]); +		cfg_req[indx].data = &cfg_data[indx]; +		switch (vty->cfg_changes[indx].operation) { +		case NB_OP_DESTROY: +			cfg_req[indx].req_type = +				MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; +			break; -			cfgreq[cnt] = &cfg_req[cnt]; -			cnt++; +		case NB_OP_CREATE: +		case NB_OP_MODIFY: +		case NB_OP_MOVE: +		case NB_OP_PRE_VALIDATE: +		case NB_OP_APPLY_FINISH: +			cfg_req[indx].req_type = +				MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; +			break; +		case NB_OP_GET_ELEM: +		case NB_OP_GET_NEXT: +		case NB_OP_GET_KEYS: +		case NB_OP_LOOKUP_ENTRY: +		case NB_OP_RPC: +		default: +			assertf(false, +				"Invalid operation type for send config: %d", +				vty->cfg_changes[indx].operation); +			/*NOTREACHED*/ +			abort();  		} -		vty->mgmt_req_id++; -		if (cnt && mgmt_fe_send_setcfg_req( -				   mgmt_fe_client, vty->mgmt_session_id, -				   vty->mgmt_req_id, MGMTD_DS_CANDIDATE, cfgreq, -				   cnt, implicit_commit, -				   MGMTD_DS_RUNNING) != MGMTD_SUCCESS) { -			zlog_err("Failed to send %d Config Xpaths to MGMTD!!", -				 (int)indx); -			vty_out(vty, "Failed to send SETCFG-REQ to MGMTD!\n"); -			return -1; -		} +		cfgreq[indx] = &cfg_req[indx]; +	} +	if (!indx) +		return 0; -		vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ"; +	vty->mgmt_req_id++; +	if (mgmt_fe_send_setcfg_req(mgmt_fe_client, vty->mgmt_session_id, +				    vty->mgmt_req_id, MGMTD_DS_CANDIDATE, +				    cfgreq, indx, implicit_commit, +				    MGMTD_DS_RUNNING)) { +		zlog_err("Failed to send %zu config xpaths to mgmtd", indx); +		vty_out(vty, "%% Failed to send commands to mgmtd\n"); +		return -1;  	} +	vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ"; +  	return 0;  }  | 
