| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <qpid/dispatch/ctools.h> |
| #include "agent_config_auto_link.h" |
| #include "route_control.h" |
| #include <inttypes.h> |
| #include <stdio.h> |
| |
| #define QDR_CONFIG_AUTO_LINK_NAME 0 |
| #define QDR_CONFIG_AUTO_LINK_IDENTITY 1 |
| #define QDR_CONFIG_AUTO_LINK_TYPE 2 |
| #define QDR_CONFIG_AUTO_LINK_ADDRESS 3 |
| #define QDR_CONFIG_AUTO_LINK_ADDR 4 |
| #define QDR_CONFIG_AUTO_LINK_DIRECTION 5 |
| #define QDR_CONFIG_AUTO_LINK_DIR 6 |
| #define QDR_CONFIG_AUTO_LINK_PHASE 7 |
| #define QDR_CONFIG_AUTO_LINK_CONNECTION 8 |
| #define QDR_CONFIG_AUTO_LINK_CONTAINER_ID 9 |
| #define QDR_CONFIG_AUTO_LINK_EXT_ADDRESS 10 |
| #define QDR_CONFIG_AUTO_LINK_EXT_ADDR 11 |
| #define QDR_CONFIG_AUTO_LINK_LINK_REF 12 |
| #define QDR_CONFIG_AUTO_LINK_OPER_STATUS 13 |
| #define QDR_CONFIG_AUTO_LINK_LAST_ERROR 14 |
| #define QDR_CONFIG_AUTO_LINK_FALLBACK 15 |
| |
| const char *qdr_config_auto_link_columns[] = |
| {"name", |
| "identity", |
| "type", |
| "address", |
| "addr", |
| "direction", |
| "dir", |
| "phase", |
| "connection", |
| "containerId", |
| "externalAddress", |
| "externalAddr", |
| "linkRef", |
| "operStatus", |
| "lastError", |
| "fallback", |
| 0}; |
| |
| const char *CONFIG_AUTOLINK_TYPE = "org.apache.qpid.dispatch.router.config.autoLink"; |
| |
| static void qdr_config_auto_link_insert_column_CT(qdr_auto_link_t *al, int col, qd_composed_field_t *body, bool as_map) |
| { |
| const char *text = 0; |
| const char *key; |
| char id_str[100]; |
| |
| if (!al) |
| return; |
| |
| if (as_map) |
| qd_compose_insert_string(body, qdr_config_auto_link_columns[col]); |
| |
| switch(col) { |
| case QDR_CONFIG_AUTO_LINK_NAME: |
| if (al->name) |
| qd_compose_insert_string(body, al->name); |
| else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_IDENTITY: |
| snprintf(id_str, 100, "%"PRId64, al->identity); |
| qd_compose_insert_string(body, id_str); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_TYPE: |
| qd_compose_insert_string(body, CONFIG_AUTOLINK_TYPE); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_ADDR: |
| case QDR_CONFIG_AUTO_LINK_ADDRESS: |
| key = (const char*) qd_hash_key_by_handle(al->addr->hash_handle); |
| if (key && key[0] == 'M') |
| qd_compose_insert_string(body, &key[2]); |
| else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_DIR: |
| case QDR_CONFIG_AUTO_LINK_DIRECTION: |
| text = al->dir == QD_INCOMING ? "in" : "out"; |
| qd_compose_insert_string(body, text); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_PHASE: |
| qd_compose_insert_int(body, al->phase); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_CONNECTION: |
| case QDR_CONFIG_AUTO_LINK_CONTAINER_ID: |
| if (al->conn_id) { |
| key = (const char*) qd_hash_key_by_handle(al->conn_id->connection_hash_handle); |
| if (!key) |
| key = (const char*) qd_hash_key_by_handle(al->conn_id->container_hash_handle); |
| |
| if (key && key[0] == 'L' && col == QDR_CONFIG_AUTO_LINK_CONNECTION) { |
| qd_compose_insert_string(body, &key[1]); |
| break; |
| } |
| if (key && key[0] == 'C' && col == QDR_CONFIG_AUTO_LINK_CONTAINER_ID) { |
| qd_compose_insert_string(body, &key[1]); |
| break; |
| } |
| } |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_EXT_ADDR: |
| case QDR_CONFIG_AUTO_LINK_EXT_ADDRESS: |
| if (al->external_addr) |
| qd_compose_insert_string(body, al->external_addr); |
| else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_LINK_REF: |
| if (al->link) { |
| snprintf(id_str, 100, "%"PRId64, al->link->identity); |
| qd_compose_insert_string(body, id_str); |
| } else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_OPER_STATUS: |
| switch (al->state) { |
| case QDR_AUTO_LINK_STATE_INACTIVE: text = "inactive"; break; |
| case QDR_AUTO_LINK_STATE_ATTACHING: text = "attaching"; break; |
| case QDR_AUTO_LINK_STATE_FAILED: text = "failed"; break; |
| case QDR_AUTO_LINK_STATE_ACTIVE: text = "active"; break; |
| case QDR_AUTO_LINK_STATE_QUIESCING: text = "quiescing"; break; |
| case QDR_AUTO_LINK_STATE_IDLE: text = "idle"; break; |
| } |
| |
| if (text) |
| qd_compose_insert_string(body, text); |
| else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_LAST_ERROR: |
| if (al->last_error) |
| qd_compose_insert_string(body, al->last_error); |
| else |
| qd_compose_insert_null(body); |
| break; |
| |
| case QDR_CONFIG_AUTO_LINK_FALLBACK: |
| qd_compose_insert_bool(body, al->fallback); |
| break; |
| } |
| } |
| |
| |
| static void qdr_agent_write_config_auto_link_CT(qdr_query_t *query, qdr_auto_link_t *al) |
| { |
| qd_composed_field_t *body = query->body; |
| |
| qd_compose_start_list(body); |
| if (al) { |
| int i = 0; |
| while (query->columns[i] >= 0) { |
| qdr_config_auto_link_insert_column_CT(al, query->columns[i], body, false); |
| i++; |
| } |
| } |
| qd_compose_end_list(body); |
| } |
| |
| |
| static void qdr_manage_advance_config_auto_link_CT(qdr_query_t *query, qdr_auto_link_t *al) |
| { |
| if (al) { |
| query->next_offset++; |
| al = DEQ_NEXT(al); |
| query->more = !!al; |
| } |
| else { |
| query->more = false; |
| } |
| } |
| |
| |
| void qdra_config_auto_link_get_first_CT(qdr_core_t *core, qdr_query_t *query, int offset) |
| { |
| // |
| // Queries that get this far will always succeed. |
| // |
| query->status = QD_AMQP_OK; |
| |
| // |
| // If the offset goes beyond the set of objects, end the query now. |
| // |
| if (offset >= DEQ_SIZE(core->auto_links)) { |
| query->more = false; |
| qdr_agent_enqueue_response_CT(core, query); |
| return; |
| } |
| |
| // |
| // Run to the object at the offset. |
| // |
| qdr_auto_link_t *al = DEQ_HEAD(core->auto_links); |
| for (int i = 0; i < offset && al; i++) |
| al = DEQ_NEXT(al); |
| assert(al); |
| |
| if (al) { |
| |
| // |
| // Write the columns of the object into the response body. |
| // |
| qdr_agent_write_config_auto_link_CT(query, al); |
| |
| // |
| // Advance to the next auto_link |
| // |
| query->next_offset = offset; |
| qdr_manage_advance_config_auto_link_CT(query, al); |
| } |
| else { |
| query->more = false; |
| } |
| |
| // |
| // Enqueue the response. |
| // |
| qdr_agent_enqueue_response_CT(core, query); |
| } |
| |
| |
| void qdra_config_auto_link_get_next_CT(qdr_core_t *core, qdr_query_t *query) |
| { |
| qdr_auto_link_t *al = 0; |
| |
| if (query->next_offset < DEQ_SIZE(core->auto_links)) { |
| al = DEQ_HEAD(core->auto_links); |
| for (int i = 0; i < query->next_offset && al; i++) |
| al = DEQ_NEXT(al); |
| } |
| |
| if (al) { |
| // |
| // Write the columns of the addr entity into the response body. |
| // |
| qdr_agent_write_config_auto_link_CT(query, al); |
| |
| // |
| // Advance to the next object |
| // |
| qdr_manage_advance_config_auto_link_CT(query, al); |
| } else |
| query->more = false; |
| |
| // |
| // Enqueue the response. |
| // |
| qdr_agent_enqueue_response_CT(core, query); |
| } |
| |
| |
| static const char *qdra_auto_link_direction_CT(qd_parsed_field_t *field, qd_direction_t *dir) |
| { |
| if (field) { |
| qd_iterator_t *iter = qd_parse_raw(field); |
| if (qd_iterator_equal(iter, (unsigned char*) "in")) { |
| *dir = QD_INCOMING; |
| return 0; |
| } else if (qd_iterator_equal(iter, (unsigned char*) "out")) { |
| *dir = QD_OUTGOING; |
| return 0; |
| } |
| return "Invalid value for 'direction'"; |
| } |
| return "Missing value for 'direction'"; |
| } |
| |
| |
| static qdr_auto_link_t *qdr_auto_link_config_find_by_identity_CT(qdr_core_t *core, qd_iterator_t *identity) |
| { |
| if (!identity) |
| return 0; |
| |
| qdr_auto_link_t *rc = DEQ_HEAD(core->auto_links); |
| while (rc) { |
| // Convert the passed in identity to a char* |
| char id[100]; |
| snprintf(id, 100, "%"PRId64, rc->identity); |
| if (qd_iterator_equal(identity, (const unsigned char*) id)) |
| break; |
| rc = DEQ_NEXT(rc); |
| } |
| |
| return rc; |
| |
| } |
| |
| |
| static qdr_auto_link_t *qdr_auto_link_config_find_by_name_CT(qdr_core_t *core, qd_iterator_t *name) |
| { |
| if (!name) |
| return 0; |
| |
| qdr_auto_link_t *rc = DEQ_HEAD(core->auto_links); |
| while (rc) { // Sometimes the name can be null |
| if (rc->name && qd_iterator_equal(name, (const unsigned char*) rc->name)) |
| break; |
| rc = DEQ_NEXT(rc); |
| } |
| |
| return rc; |
| } |
| |
| |
| void qdra_config_auto_link_delete_CT(qdr_core_t *core, |
| qdr_query_t *query, |
| qd_iterator_t *name, |
| qd_iterator_t *identity) |
| { |
| qdr_auto_link_t *al = 0; |
| |
| if (!name && !identity) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "No name or identity provided"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing DELETE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| } |
| else { |
| if (identity) |
| al = qdr_auto_link_config_find_by_identity_CT(core, identity); |
| else if (name) |
| al = qdr_auto_link_config_find_by_name_CT(core, name); |
| |
| if (al) { |
| qdr_route_del_auto_link_CT(core, al); |
| query->status = QD_AMQP_NO_CONTENT; |
| } else |
| query->status = QD_AMQP_NOT_FOUND; |
| } |
| |
| // |
| // Enqueue the response. |
| // |
| qdr_agent_enqueue_response_CT(core, query); |
| } |
| |
| void qdra_config_auto_link_create_CT(qdr_core_t *core, |
| qd_iterator_t *name, |
| qdr_query_t *query, |
| qd_parsed_field_t *in_body) |
| { |
| while (true) { |
| // |
| // Ensure there isn't a duplicate name and that the body is a map |
| // |
| qdr_auto_link_t *al = DEQ_HEAD(core->auto_links); |
| while (al) { |
| if (name && al->name && qd_iterator_equal(name, (const unsigned char*) al->name)) |
| break; |
| al = DEQ_NEXT(al); |
| } |
| |
| if (!!al) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "Name conflicts with an existing entity"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| if (!qd_parse_is_map(in_body)) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "Body of request must be a map"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| // |
| // Extract the fields from the request |
| // |
| qd_parsed_field_t *addr_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_ADDRESS]); |
| if (!addr_field) { |
| addr_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_ADDR]); |
| if (addr_field) |
| qd_log(core->agent_log, QD_LOG_WARNING, "The 'addr' attribute of autoLink has been deprecated. Use 'address' instead"); |
| } |
| qd_parsed_field_t *dir_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_DIRECTION]); |
| if (! dir_field) { |
| dir_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_DIR]); |
| if (dir_field) |
| qd_log(core->agent_log, QD_LOG_WARNING, "The 'dir' attribute of autoLink has been deprecated. Use 'direction' instead"); |
| } |
| qd_parsed_field_t *phase_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_PHASE]); |
| qd_parsed_field_t *connection_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_CONNECTION]); |
| qd_parsed_field_t *container_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_CONTAINER_ID]); |
| qd_parsed_field_t *fallback_field = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_FALLBACK]); |
| |
| qd_parsed_field_t *external_addr = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_EXT_ADDRESS]); |
| if (!external_addr) { |
| external_addr = qd_parse_value_by_key(in_body, qdr_config_auto_link_columns[QDR_CONFIG_AUTO_LINK_EXT_ADDR]); |
| if (external_addr) |
| qd_log(core->agent_log, QD_LOG_WARNING, "The 'externalAddr' attribute of autoLink has been deprecated. Use 'externalAddress' instead"); |
| } |
| |
| |
| if (connection_field && container_field) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "Both connection and containerId cannot be specified. Specify only one"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| bool fallback = !!fallback_field ? qd_parse_as_bool(fallback_field) : false; |
| |
| // |
| // Addr and direction fields are mandatory. Fail if they're not both here. |
| // |
| if (!addr_field || !dir_field) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "addr and direction fields are mandatory"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| qd_direction_t dir; |
| const char *error = qdra_auto_link_direction_CT(dir_field, &dir); |
| if (error) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = error; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| // |
| // Use the specified phase if present. Otherwise default based on the direction: |
| // Phase 0 for outgoing links and phase 1 for incoming links. |
| // |
| long phase = phase_field ? qd_parse_as_long(phase_field) : ((dir == QD_OUTGOING || !!fallback) ? 0 : 1); |
| |
| // |
| // Validate the phase |
| // |
| if (phase < 0 || phase > 9) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "autoLink phase must be between 0 and 9"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing CREATE of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| break; |
| } |
| |
| // |
| // The request is good. Create the entity. |
| // |
| al = qdr_route_add_auto_link_CT(core, name, addr_field, dir, phase, container_field, connection_field, external_addr, fallback); |
| |
| // |
| // Compose the result map for the response. |
| // |
| if (query->body) { |
| qd_compose_start_map(query->body); |
| for (int col = 0; col < QDR_CONFIG_AUTO_LINK_COLUMN_COUNT; col++) |
| qdr_config_auto_link_insert_column_CT(al, col, query->body, true); |
| qd_compose_end_map(query->body); |
| } |
| |
| query->status = QD_AMQP_CREATED; |
| break; |
| } |
| |
| // |
| // Enqueue the response if there is a body. If there is no body, this is a management |
| // operation created internally by the configuration file parser. |
| // |
| if (query->body) { |
| // |
| // If there was an error in processing the create, insert a NULL value into the body. |
| // |
| if (query->status.status / 100 > 2) |
| qd_compose_insert_null(query->body); |
| qdr_agent_enqueue_response_CT(core, query); |
| } else { |
| if (query->status.status / 100 > 2) |
| qd_log(core->log, QD_LOG_ERROR, "Error configuring linkRoute: %s", query->status.description); |
| qdr_query_free(query); |
| } |
| } |
| |
| |
| static void qdr_manage_write_config_auto_link_map_CT(qdr_core_t *core, |
| qdr_auto_link_t *al, |
| qd_composed_field_t *body, |
| const char *qdr_config_auto_link_columns[]) |
| { |
| qd_compose_start_map(body); |
| |
| for(int i = 0; i < QDR_CONFIG_AUTO_LINK_COLUMN_COUNT; i++) { |
| qd_compose_insert_string(body, qdr_config_auto_link_columns[i]); |
| qdr_config_auto_link_insert_column_CT(al, i, body, false); |
| } |
| |
| qd_compose_end_map(body); |
| } |
| |
| |
| void qdra_config_auto_link_get_CT(qdr_core_t *core, |
| qd_iterator_t *name, |
| qd_iterator_t *identity, |
| qdr_query_t *query, |
| const char *qdr_config_auto_link_columns[]) |
| { |
| qdr_auto_link_t *al = 0; |
| |
| if (!name && !identity) { |
| query->status = QD_AMQP_BAD_REQUEST; |
| query->status.description = "No name or identity provided"; |
| qd_log(core->agent_log, QD_LOG_ERROR, "Error performing READ of %s: %s", CONFIG_AUTOLINK_TYPE, query->status.description); |
| } |
| else { |
| if (identity) //If there is identity, ignore the name |
| al = qdr_auto_link_config_find_by_identity_CT(core, identity); |
| else if (name) |
| al = qdr_auto_link_config_find_by_name_CT(core, name); |
| |
| if (al == 0) { |
| // Send back a 404 |
| query->status = QD_AMQP_NOT_FOUND; |
| } |
| else { |
| // |
| // Write the columns of the address entity into the response body. |
| // |
| qdr_manage_write_config_auto_link_map_CT(core, al, query->body, qdr_config_auto_link_columns); |
| query->status = QD_AMQP_OK; |
| } |
| } |
| |
| // |
| // Enqueue the response. |
| // |
| qdr_agent_enqueue_response_CT(core, query); |
| |
| } |