/* -*- mode:c -*- */ /** @file ** @brief Maestro Attribute Schema ingest and data structures **/ /* * Copyright (C) 2020 Cray Computer GmbH * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "maestro/status.h" #include "maestro/logging.h" #include "maestro/i_uthash.h" #include "protocols/mstro_pool.pb-c.h" #include "yaml.h" #include <errno.h> #include <stdlib.h> /* A single attribute definition */ struct mstro_attribute_entry { UT_hash_handle *hh; /**< hashable, by key, using magic hash function * below */ char *key; /**< attribute key, also key in hash table */ /** The type permitted for this attribute */ Mstro__Pool__AVal__ValCase val_type; }; /* Data structure for (a fragment of) a schema */ struct mstro_attribute_schema { /** The name of this schema */ char *name; /** Its version number */ size_t version; /** the set of declarations in this schema */ struct mstro_attribite_entry *declarations; }; /* The (merge of) all loaded schemata. This is conceptually read-only * after mstro_init(), and holds the information on all defined * attribute keys and value types */ static struct mstro_attribute_schema *g_mstro_attribute_schema = NULL; /** Initialize schema module */ mstro_status mstro_schema_init(void) { return MSTRO_OK; } /** finalize schema module */ mstro_status mstro_schema_finalize(void) { if(g_mstro_attribute_schema!=NULL) { WARN("Not deallocating schema tree\n"); } return MSTRO_OK; } static inline mstro_status mstro_schema__parse(yaml_parser_t parser, struct mstro_attribute_schema **result) { yaml_token_t token; char *last_key=NULL; enum { INVALID, KEY, VALUE, BLOCKSEQ, BLOCKENT, BLOCKMAP } last_kind = INVALID; ssize_t depth = -1; *result = malloc(sizeof(struct mstro_attribute_schema)); if(*result == NULL) { return MSTRO_NOMEM; } /* for now, let's see if token-based scanning is sufficient */ do { yaml_parser_scan(&parser, &token); switch(token.type) { /* Stream start/end */ case YAML_STREAM_START_TOKEN: DEBUG("STREAM START\n"); depth++; break; case YAML_STREAM_END_TOKEN: DEBUG("STREAM END\n"); depth--; break; /* Token types (read before actual token) */ case YAML_KEY_TOKEN: DEBUG("(Key token) \n"); last_kind = KEY; break; case YAML_VALUE_TOKEN: DEBUG("(Value token) \n"); last_kind = VALUE; break; /* Block delimeters */ case YAML_BLOCK_SEQUENCE_START_TOKEN: DEBUG("Start Block (Sequence)\n"); last_kind = BLOCKSEQ; depth++; break; case YAML_BLOCK_ENTRY_TOKEN: DEBUG("Start Block (Entry)\n"); last_kind = BLOCKENT; break; case YAML_BLOCK_END_TOKEN: DEBUG("End block\n"); depth--; break; /* Data */ case YAML_BLOCK_MAPPING_START_TOKEN: DEBUG("[Block mapping]\n"); last_kind=BLOCKMAP; depth++; break; case YAML_SCALAR_TOKEN: DEBUG("scalar %s (for kind %d)\n", token.data.scalar.value, last_kind); /* yaml_char_t is signed */ const char *val = (const char*)token.data.scalar.value; switch(last_kind) { case KEY: /* store key for value coming up */ if(last_key!=NULL) { ERR("two keys in succession: stack %s, here %s\n", last_key, val); free(last_key); } last_key = strdup(val); if(last_key==NULL) { ERR("Failed to allocate key\n"); } break; case VALUE: if(last_key==NULL) { ERR("No key preceeded this value\n"); } else { /* This is where the built-in meta-schema is hiding */ if(strcmp(last_key, "schema-name")==0) { (*result)->name = last_key; last_key=NULL; } else if(strcmp(last_key, "schema-version")==0) { (*result)->version = atol(val); free(last_key); } else { DEBUG("Value |%s| for unknown key |%s|\n", val, last_key); free(last_key); } last_key=NULL; } break; default: WARN("Unexpected last_kind value\n"); } break; /* Others */ default: WARN("Got token of type %d\n", token.type); } if(token.type != YAML_STREAM_END_TOKEN) yaml_token_delete(&token); } while(token.type != YAML_STREAM_END_TOKEN); yaml_token_delete(&token); if(depth!=-1) { ERR("YAML stream nesting does not balance, depth %u at end (should be -1)\n", depth); } return MSTRO_UNIMPL; } /* merge SCHEMA into global schema tree. Consume argument, even on error */ static inline mstro_status mstro_schema__merge(struct mstro_attribute_schema *schema) { return MSTRO_UNIMPL; } /** Add a file's content to schema tree */ mstro_status mstro_schema_add_from_file(char *path) { mstro_status s; if(path==NULL) { s=MSTRO_INVARG; goto BAILOUT; } FILE *f = fopen(path, "r"); if(f==NULL) { ERR("Failed to open schema file %s: %d (%s)\n", path, errno, strerror(errno)); s=MSTRO_FAIL; goto BAILOUT; } /* Initialize parser */ yaml_parser_t parser; if(!yaml_parser_initialize(&parser)) { ERR("Failed to initialize YAML parser\n"); s= MSTRO_FAIL; goto BAILOUT_CLOSE; } /* Set input file */ yaml_parser_set_input_file(&parser, f); struct mstro_attribute_schema *schema; s=mstro_schema__parse(parser, &schema); yaml_parser_delete(&parser); if(s!=MSTRO_OK) { ERR("Failed to parse schema %s\n", path); } else { INFO("Parsed schema definition %s\n", schema->name); /* merge it (consumes argument, even on error) */ s=mstro_schema__merge(schema); if(s!=MSTRO_OK) { ERR("Failed to merge schema\n"); } } BAILOUT_CLOSE: fclose(f); BAILOUT: return s; }