/*
* iSNS object model - portal group specific code
*
* Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
*/
#include <stdlib.h>
#include <string.h>
#include <libisns/isns.h>
#include "objects.h"
#include "vendor.h"
#include <libisns/attrs.h>
#include <libisns/util.h>
/* For relationship stuff - should go */
#include "db.h"
/*
* Retrieve attribute @old_tag from object @obj, create a copy with
* tag @new_tag, and append it to list @dst.
* (Helper function for the portal group stuff)
*/
static int
__isns_object_translate_attr(isns_object_t *obj,
uint32_t old_tag, uint32_t new_tag,
isns_attr_list_t *dst)
{
isns_value_t value;
if (!isns_attr_list_get_value(&obj->ie_attrs, old_tag, &value))
return 0;
isns_attr_list_append_value(dst, new_tag, NULL, &value);
return 1;
}
/*
* Portal Group
*/
static isns_object_t *
__isns_pg_create(const isns_attr_list_t *attrs, uint32_t pg_tag,
isns_object_t *portal, isns_object_t *node)
{
isns_object_t *obj;
obj = isns_create_object(&isns_iscsi_pg_template, attrs,
isns_object_get_entity(portal));
/*
* 3.4
*
* Each Portal and iSCSI Storage Node registered in an Entity can
* be associated using a Portal Group (PG) object. The PG Tag
* (PGT), if non-NULL, indicates that the associated Portal
* provides access to the associated iSCSI Storage Node in
* the Entity. All Portals that have the same PGT value for
* a specific iSCSI Storage Node allow coordinated access to
* that node.
*
* 5.6.5.2
*
* If the PGT of the Portal Group is not NULL, then a relationship
* exists between the indicated Storage Node and Portal; if the
* PGT is NULL, then no relationship exists.
*/
if (pg_tag != 0) {
isns_object_set_uint32(obj,
ISNS_TAG_PG_TAG, pg_tag);
} else {
/* A NULL PGT indicates that the
* storage node cannot be accessed through
* this portal. */
isns_object_set_nil(obj, ISNS_TAG_PG_TAG);
}
/* This object represents a relationship between portal
and storage node. Create a relation. */
obj->ie_relation = isns_create_relation(obj,
ISNS_RELATION_PORTAL_GROUP,
portal, node);
return obj;
}
/*
* Find the portal for a given portal group
*/
static isns_object_t *
__isns_pg_find_portal(isns_db_t *db, isns_object_t *pg,
const isns_object_list_t *extra_objs)
{
isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
isns_object_t *obj = NULL;
/* FIXME: ISNS_TAG_PG_PORTAL_IP_ADDR -> ...ADDRESS */
if (!__isns_object_translate_attr(pg,
ISNS_TAG_PG_PORTAL_IP_ADDR,
ISNS_TAG_PORTAL_IP_ADDRESS,
&key_attrs))
goto out;
if (!__isns_object_translate_attr(pg,
ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
ISNS_TAG_PORTAL_TCP_UDP_PORT,
&key_attrs))
goto out;
obj = isns_db_lookup(db, &isns_portal_template, &key_attrs);
if (!obj && extra_objs)
obj = isns_object_list_lookup(extra_objs,
&isns_portal_template, &key_attrs);
out:
isns_attr_list_destroy(&key_attrs);
return obj;
}
/*
* Find the node for a given portal group
*/
static isns_object_t *
__isns_pg_find_node(isns_db_t *db, isns_object_t *pg,
const isns_object_list_t *extra_objs)
{
isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
isns_object_t *obj = NULL;
if (!__isns_object_translate_attr(pg,
ISNS_TAG_PG_ISCSI_NAME,
ISNS_TAG_ISCSI_NAME,
&key_attrs))
goto out;
obj = isns_db_lookup(db, &isns_iscsi_node_template, &key_attrs);
if (!obj && extra_objs)
obj = isns_object_list_lookup(extra_objs,
&isns_iscsi_node_template, &key_attrs);
out:
isns_attr_list_destroy(&key_attrs);
return obj;
}
/*
* When creating a portal group, it must not connect nodes and
* portals from other entities. However, it is perfectly fine to
* link objects in limbo.
*/
static inline int
__isns_pg_may_relate(isns_object_t *entity, isns_object_t *subordinate)
{
isns_object_t *other;
other = isns_object_get_entity(subordinate);
return other == NULL || other == entity;
}
/*
* Given a portal group object, create the relationship
*/
isns_relation_t *
isns_db_build_pg_relation(isns_db_t *db, isns_object_t *pg,
const isns_object_list_t *extra_objs)
{
isns_object_t *entity, *node = NULL, *portal = NULL;
entity = isns_object_get_entity(pg);
node = __isns_pg_find_node(db, pg, extra_objs);
if (node == NULL) {
isns_error("Trying to register PG for non-existant node\n");
goto failed;
}
if (!__isns_pg_may_relate(entity, node)) {
isns_error("Trying to register PG for node in other entity\n");
goto failed;
}
portal = __isns_pg_find_portal(db, pg, extra_objs);
if (portal == NULL) {
isns_error("Trying to register PG for non-existant portal\n");
goto failed;
}
if (!__isns_pg_may_relate(entity, portal)) {
isns_error("Trying to register PG for portal in other entity\n");
goto failed;
}
pg->ie_relation = isns_create_relation(pg,
ISNS_RELATION_PORTAL_GROUP,
node, portal);
isns_object_release(portal);
isns_object_release(node);
return pg->ie_relation;
failed:
if (portal)
isns_object_release(portal);
if (node)
isns_object_release(node);
return NULL;
}
/*
* Create a portal group given node, portal and PGT
*/
isns_object_t *
isns_create_portal_group(isns_object_t *portal,
isns_object_t *node, uint32_t pg_tag)
{
isns_attr_list_t key_attrs = ISNS_ATTR_LIST_INIT;
isns_object_t *obj = NULL;
if (portal == NULL || node == NULL)
return NULL;
if (node->ie_container != portal->ie_container) {
isns_error("Refusing to create portal group "
"linking objects from different entities\n");
return NULL;
}
if (__isns_object_translate_attr(node,
ISNS_TAG_ISCSI_NAME,
ISNS_TAG_PG_ISCSI_NAME,
&key_attrs)
&& __isns_object_translate_attr(portal,
ISNS_TAG_PORTAL_IP_ADDRESS,
ISNS_TAG_PG_PORTAL_IP_ADDR,
&key_attrs)
&& __isns_object_translate_attr(portal,
ISNS_TAG_PORTAL_TCP_UDP_PORT,
ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
&key_attrs)) {
obj = __isns_pg_create(&key_attrs, pg_tag, portal, node);
}
isns_attr_list_destroy(&key_attrs);
return obj;
}
/*
* 5.6.5.1
* New PG objects are registered when an associated Portal or
* iSCSI Node object is registered. An explicit PG object
* registration MAY follow a Portal or iSCSI Node object
* registration in a DevAttrReg message.
* [...]
* If the PGT value is not included in the Storage Node or
* Portal object registration, and if a PGT value was not
* previously registered for the relationship, then the PGT for
* the corresponding PG object SHALL be registered with a value
* of 0x00000001.
*
* We return non-NULL if the object was created.
*/
isns_object_t *
isns_create_default_portal_group(isns_db_t *db,
isns_object_t *portal, isns_object_t *node)
{
isns_object_t *obj;
if (portal == NULL || node == NULL)
return 0;
/* See if there is a PG already */
obj = isns_db_get_relationship_object(db, node, portal,
ISNS_RELATION_PORTAL_GROUP);
if (obj != NULL) {
isns_object_release(obj);
return NULL;
}
return isns_create_portal_group(portal, node, 1);
}
static uint32_t iscsi_pg_attrs[] = {
ISNS_TAG_PG_ISCSI_NAME,
ISNS_TAG_PG_PORTAL_IP_ADDR,
ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
ISNS_TAG_PG_TAG,
ISNS_TAG_PG_INDEX,
};
static uint32_t iscsi_pg_key_attrs[] = {
ISNS_TAG_PG_ISCSI_NAME,
ISNS_TAG_PG_PORTAL_IP_ADDR,
ISNS_TAG_PG_PORTAL_TCP_UDP_PORT,
};
isns_object_template_t isns_iscsi_pg_template = {
.iot_name = "iSCSI Portal Group",
.iot_handle = ISNS_OBJECT_TYPE_PG,
.iot_attrs = iscsi_pg_attrs,
.iot_num_attrs = array_num_elements(iscsi_pg_attrs),
.iot_keys = iscsi_pg_key_attrs,
.iot_num_keys = array_num_elements(iscsi_pg_key_attrs),
.iot_attrs = iscsi_pg_attrs,
.iot_keys = iscsi_pg_key_attrs,
.iot_index = ISNS_TAG_PG_INDEX,
.iot_next_index = ISNS_TAG_PG_NEXT_INDEX,
.iot_container = &isns_entity_template,
.iot_relation_type = ISNS_RELATION_PORTAL_GROUP,
.iot_build_relation = isns_db_build_pg_relation,
};