static GList* zapi_list_aggregates(na_server_t *s, GError **err) { GList *la; na_elem_t *out, *in; na_elem_t *aggr_info_array, *aggr_info; na_elem_iter_t iter_aggr; XTRACE("Entering"); in = na_elem_new("aggr-list-info"); out = na_server_invoke_elem(s, in); na_elem_free(in); if (!out) { GSETERROR(err, "ZAPI error : no output"); LOG_RETURN(NULL,"Failure (zapi error)"); } if (na_results_status(out) != NA_OK) { na_elem_free(out); GSETERROR(err, "NetApp filer error : (%d) %s", na_results_errno(out), na_results_reason(out)); LOG_RETURN(NULL,"Failure (server)"); } la = NULL; aggr_info_array = na_elem_child(out, "aggregates"); for (iter_aggr=na_child_iterator(aggr_info_array); (aggr_info=na_iterator_next(&iter_aggr)) ;) { const char *aname; aname = na_child_get_string(aggr_info,"name"); la = g_list_append(la, g_strdup(aname)); } na_elem_free(out); LOG_RETURN(la,"Success"); }
struct volume_s* netapp_get_volume(struct filer_s *filer, const char *name, GError **error) { register int rc; struct volume_s **vol_ptr; struct enterprise_s *enterprise; XTRACE("Entering"); enterprise = filer->enterprise; if (!netapp_refresh_fixed_filer_data(filer, error)) { GSETERROR(error, "Uncomplete or too old filer data"); LOG_RETURN(NULL, "Failure (uncomplete info)"); } for (vol_ptr=filer->ctx->fixed.volumes; *vol_ptr ;vol_ptr++) { XTRACE("Comparing [%s] to [%s]", enterprise->get_name(*vol_ptr), name); rc = g_ascii_strcasecmp(enterprise->get_name(*vol_ptr), name); if (rc == 0) return *vol_ptr; } LOG_RETURN(NULL, "Failure (not found)"); }
static gboolean netapp_refresh_volumes_list(struct filer_s *filer, GError **error) { struct volume_s **new_volumes, **ptr_vol, **old_vol; XTRACE("Entering"); if (!(new_volumes = netapp_load_volumes(filer, error))) { GSETERROR(error, "Failed to refresh the volume list"); LOG_RETURN(FALSE,"Failure (volumes list loading)"); } if (!filer->ctx->fixed.volumes) { filer->ctx->fixed.volumes = new_volumes; LOG_RETURN(TRUE,"Success (first loading)"); } /* merge the old volumes on the old volumes in the new, * this will copy the volume statistics, then keep only * the latest volume list */ for (ptr_vol=new_volumes; *ptr_vol ;ptr_vol++) { for (old_vol=filer->ctx->fixed.volumes; *old_vol ;old_vol++) { if (0 == g_ascii_strcasecmp((*ptr_vol)->path, (*old_vol)->path)) { memcpy(*ptr_vol, *old_vol, sizeof(struct volume_s)); break; } } } netapp_free_volume_array(filer->ctx->fixed.volumes); filer->ctx->fixed.volumes = new_volumes; LOG_RETURN(TRUE,"Success (reload)"); }
/** * Get the prompt string. */ char* get_prompt() { LOG_ENTRY; getcwd(prompt_buffer, PROMPT_BUFFER_SIZE); #if DEBUG LOG_RETURN(prompt_buffer); #else LOG_RETURN(strrchr(prompt_buffer, '/') + 1); #endif }
/** * Returns true if a string is all whitespace characters. */ bool str_is_whitespace(char* str) { LOG_ENTRY; char* c; for (c = str; c && *c; c++) { if (!(*c == ' ' || *c == '\t' || *c == '\n')) { LOG_RETURN(false); } } LOG_RETURN(true); }
static GHashTable* zapi_get_disk2aggr(na_server_t *s, GError **err) { GHashTable *ht; na_elem_t *out, *in; na_elem_t *disk_info_array, *disk_info; na_elem_iter_t iter_disk; XTRACE("Entering"); /* the target aggregate is optional, and that is fine, we will * get the information about all the agregates */ in = na_elem_new("disk-list-info"); out = na_server_invoke_elem(s, in); /* Error management */ if (!out) { na_elem_free(in); GSETERROR(err, "ZAPI error : no output"); LOG_RETURN(NULL,"Failure (zapi error)"); } if (na_results_status(out) != NA_OK) { na_elem_free(in); na_elem_free(out); GSETERROR(err, "NetApp filer error : (%d) %s", na_results_errno(out), na_results_reason(out)); LOG_RETURN(NULL,"Failure (server)"); } /* Reply's content handling */ ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); disk_info_array = na_elem_child(out,"disk-details"); for (iter_disk=na_child_iterator(disk_info_array); (disk_info=na_iterator_next(&iter_disk)) ;) { const char *aname, *dname; /* normal case for flex-vol */ aname = na_child_get_string(disk_info,"aggregate"); if (!aname) /* special case for trad-vol volumes */ aname = na_child_get_string(disk_info,"volume"); if (aname) { dname = na_child_get_string(disk_info,"disk-uid"); g_hash_table_insert(ht, g_strdup(dname), g_strdup(aname)); XTRACE("Saved : disk[%s] -> aggr[%s]", dname, aname); } } na_elem_free(in); na_elem_free(out); LOG_RETURN(ht,"Success"); }
/** * Change Directory. */ bool change_directory(void) { LOG_ENTRY; int input_length = strlen(input_buffer); char target_dir [input_length - 3]; memcpy(target_dir, input_buffer + 3, input_length - 3); target_dir[input_length-3-1] = '\0'; if(chdir(target_dir)) { printf("%s: %s", CHANGE_DIR_ERROR_MSG, strerror(errno)); LOG_RETURN(false); } LOG_RETURN(true); }
/** * Handle Input. */ bool handle_input(void) { LOG_ENTRY; if (is_quit_command(input_buffer)) { exit_shell(); LOG_RETURN(false); } else if (is_cd_command(input_buffer)) { change_directory(); LOG_RETURN(false); } else if (str_is_whitespace(input_buffer)) { LOG_RETURN(false); } else { input_tokens = tokenize(input_buffer); LOG_RETURN(true); } }
gboolean netapp_api_init(struct enterprise_s *e, GError **err) { gchar str_err[1024]; (void) e; memset(str_err, 0x00, sizeof(str_err)); if (!na_startup(str_err, sizeof(str_err))) { GSETERROR(err, "Netapp OnTap management API failure init : %.*s", sizeof(str_err), str_err); LOG_RETURN(FALSE,"Failure"); } LOG_RETURN(TRUE,"Success"); }
/** * Handle exit status. */ void handle_exit_status(int status) { LOG_ENTRY; if (WEXITSTATUS(status)) { printf("%s %d\n", CHILD_STATUS_ERROR_MSG, WEXITSTATUS(status)); } LOG_RETURN(); }
static gboolean netapp_refresh_fixed_filer_data(struct filer_s *filer, GError **err) { register gboolean time_is_up; time_is_up = filer->ctx->fixed.last_update + 60L < time(0); if (time_is_up || !filer->ctx->fixed.volumes) { GHashTable *ht_vol2aggr, *ht_disk2aggr; GList *aggregates; if (!netapp_refresh_network_definitions(filer, err)) LOG_RETURN(FALSE,"Failure (network)"); if (!netapp_refresh_volumes_list(filer, err)) LOG_RETURN(FALSE,"Failure (volumes)"); aggregates = zapi_list_aggregates(filer->ctx->na_session, err); if (aggregates) { if (filer->ctx->fixed.aggregates) { g_list_foreach (filer->ctx->fixed.aggregates, (GFunc)g_free, NULL); g_list_free(filer->ctx->fixed.aggregates); } filer->ctx->fixed.aggregates = aggregates; } ht_vol2aggr = zapi_get_vol2aggr(filer->ctx->na_session, err); if (ht_vol2aggr) { if (filer->ctx->fixed.vol2aggr) g_hash_table_destroy(filer->ctx->fixed.vol2aggr); filer->ctx->fixed.vol2aggr = ht_vol2aggr; XTRACE("Mappings saved : vol2aggr (%u)", g_hash_table_size(filer->ctx->fixed.vol2aggr)); } ht_disk2aggr = zapi_get_disk2aggr(filer->ctx->na_session, err); if (ht_disk2aggr) { if (filer->ctx->fixed.disk2aggr) g_hash_table_destroy(filer->ctx->fixed.disk2aggr); filer->ctx->fixed.disk2aggr = ht_disk2aggr; XTRACE("Mappings saved : disk2aggr (%u)", g_hash_table_size(filer->ctx->fixed.disk2aggr)); } } filer->ctx->fixed.last_update = time(0); LOG_RETURN(TRUE, "Success (filer fixed data reloaded)"); }
/* Retrieve a list of Aggregate-to-Volume list * covering all the aggregates of the given filer */ static GHashTable* zapi_get_vol2aggr(na_server_t *s, GError **err) { GHashTable *ht; na_elem_t *out, *in; na_elem_t *vol_info_array, *vol_info, *aggr_info_array, *aggr_info; na_elem_iter_t iter_aggr, iter_vol; XTRACE("Entering"); /* the target aggregate is optional, and that is fine, we will * get the information about all the agregates */ in = na_elem_new("aggr-list-info"); out = na_server_invoke_elem(s, in); na_elem_free(in); /* Error management */ if (!out) { GSETERROR(err, "ZAPI error : no output"); LOG_RETURN(NULL,"Failure (zapi error)"); } if (na_results_status(out) != NA_OK) { na_elem_free(out); GSETERROR(err, "NetApp filer error : (%d) %s", na_results_errno(out), na_results_reason(out)); LOG_RETURN(NULL,"Failure (server)"); } /* Reply's content handling */ ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); aggr_info_array = na_elem_child(out, "aggregates"); for (iter_aggr=na_child_iterator(aggr_info_array); (aggr_info=na_iterator_next(&iter_aggr)) ;) { vol_info_array = na_elem_child(aggr_info,"volumes"); for (iter_vol=na_child_iterator(vol_info_array); (vol_info=na_iterator_next(&iter_vol)) ;) { const char *vname, *aname; vname = na_child_get_string(vol_info,"name"); aname = na_child_get_string(aggr_info,"name"); g_hash_table_insert(ht, g_strdup(vname), g_strdup(aname)); XTRACE("Saved : vol[%s] -> agr[%s]", vname, aname); } } na_elem_free(out); LOG_RETURN(ht,"Success"); }
gboolean netapp_api_close(struct enterprise_s *e, GError **err) { (void) e; (void) err; XTRACE("Entering"); na_shutdown(); LOG_RETURN(TRUE,"Success"); }
gboolean netapp_monitor_volume(struct volume_s *vol, struct volume_statistics_s *st, GError **err) { struct filer_s *filer; XTRACE("Entering [%s -> %s]", vol->path, vol->name); filer = vol->filer; if (!netapp_refresh_fixed_filer_data(filer, err)) { GSETERROR(err, "Uncomplete or too old filer data"); LOG_RETURN(FALSE, "Failure (uncomplete info)"); } /* Collect FS-dependant DATA */ if (!snmp_get_template_int(filer->ctx->session, oid_fsUsedSpace, oid_fsUsedSpace_size, netapp_get_volume_id(vol), &(st->used_space), err)) { GSETERROR(err, "Failed to get the FS usage of this volume"); LOG_RETURN(FALSE,"Failure (FS usage)"); } if (!snmp_get_template_int(filer->ctx->session, oid_fsFreeSpace, oid_fsFreeSpace_size, netapp_get_volume_id(vol), &(st->free_space), err)) { GSETERROR(err, "Failed to get the FS availability of this volume"); LOG_RETURN(FALSE,"Failure (FS availability)"); } XTRACE("Space usage : used=%"G_GINT64_FORMAT" free=%"G_GINT64_FORMAT, st->used_space, st->free_space); /* Now get the Filer-dependant data */ st->cpu_idle = filer->ctx->variable.cpu_idle; st->net_idle = filer->ctx->variable.net_idle; st->io_idle = netapp_get_volume_disk_idle(filer, vol, err); if (st->io_idle < 0) { GSETERROR(err, "Failed to collect the disk-idle for vol [%s]", vol->path); LOG_RETURN(FALSE,"Failure (disk idle)"); } st->perf_idle = 100LL; LOG_RETURN(TRUE, "Success (net=%"G_GINT64_FORMAT" cpu=%"G_GINT64_FORMAT")", st->net_idle, st->cpu_idle); }
static gboolean netapp_refresh_network_definitions(struct filer_s *filer, GError **err) { oid itfIndex; gint64 itfSpeed; XTRACE("Entering"); if (!snmp_get_interface_index(filer->ctx->session, &itfIndex, err)) { GSETERROR(err, "Interface index not found"); LOG_RETURN(FALSE,"Failure (interface index)"); } if (!snmp_get_interface_speed(filer->ctx->session, itfIndex, &itfSpeed, err)) { GSETERROR(err, "Interface speed not found for index=%u", itfIndex); LOG_RETURN(FALSE,"Failure (interface bandwith)"); } filer->ctx->fixed.net_itf_index = itfIndex; filer->ctx->fixed.net_in_max = filer->ctx->fixed.net_out_max = itfSpeed; LOG_RETURN(TRUE,"Success"); }
static struct filer_ctx_s* netapp_init_filer(struct filer_s *filer, GError **err) { struct filer_ctx_s *ctx; XTRACE("Entering"); (void) filer; ctx = g_try_malloc0(sizeof(*ctx)); if (!ctx) { GSETERROR(err,"Memory allocation failure"); LOG_RETURN(NULL,"Failure (memory)"); } /* Inits the SNMP session for this Filer */ ctx->session = snmp_init(&(ctx->snmp_session), filer->str_addr, &(filer->auth.snmp), err); if (!ctx->session) { g_free(ctx); GSETERROR(err,"SNMP session error"); LOG_RETURN(NULL,"Failure (snmp)"); } /* Inits the Zapi session */ ctx->na_session = na_server_open(filer->str_addr, 1, 0); if (!ctx->na_session) { snmp_close(ctx->session); g_free(ctx); GSETERROR(err,"OnTap Management API error : failed to start a session"); LOG_RETURN(NULL,"Failure (na_session)"); } na_server_style(ctx->na_session, NA_STYLE_LOGIN_PASSWORD); na_server_set_transport_type(ctx->na_session, NA_SERVER_TRANSPORT_HTTP, NULL); na_server_adminuser(ctx->na_session, filer->auth.filer.user, filer->auth.filer.passwd); return ctx; }
/** * Free Input Tokens. */ void free_input_tokens(void) { LOG_ENTRY; #if DEBUG int free_count = 0; #endif if (input_tokens && *input_tokens) { char** foo; for (foo = input_tokens; foo && *foo; foo++) { #if DEBUG free_count++; #endif free(*foo); } #if DEBUG printf("%d tokens freed\n", free_count); #endif free(input_tokens); } LOG_RETURN(); }
/** * Main */ int main(int argc, char* argv[], char* envp[]) { LOG_ENTRY; parse_args(argc, argv); input_buffer = calloc(INPUT_BUFFER_SIZE, sizeof(char)); prompt_buffer = calloc(PROMPT_BUFFER_SIZE, sizeof(char)); while(1) { display_prompt(); if(read_input() != NULL) { bool valid = handle_input(); if (valid) { pid_t child_pid = fork(); int status; if(child_pid) { //parent-execution struct rusage child_info; wait4(child_pid, &status, 0, &child_info); if (display_child_time) { printf(CHILD_EXECUTION_TIME_FMT, (float) (child_info.ru_stime.tv_sec + child_info.ru_utime.tv_sec) + (float) (child_info.ru_stime.tv_usec + child_info.ru_utime.tv_usec) / 1000000); } CHILD_OUT_END; handle_exit_status(status); } else { //child execution child_execute_input(envp); } } } else { printf(EOF_ERROR_MSG); exit_shell(); } #if DEBUG dump_buffers(); #endif } LOG_RETURN(0); }
static GHashTable* zapi_get_disk2idle(na_server_t *s, GError **err) { GHashTable *ht; na_elem_t *out, *in; na_elem_t *instances, *instance; na_elem_iter_t iter_c, iter_i; XTRACE("Entering"); in = na_elem_new("perf-object-get-instances"); na_child_add_string(in, "objectname", "disk"); do { na_elem_t *counters = na_elem_new("counters"); na_child_add_string(counters, "counter", "disk_busy"); na_child_add_string(counters, "counter", "base_for_disk_busy"); na_child_add(in, counters); } while (0); out = na_server_invoke_elem(s, in); na_elem_free(in); /* Error management */ if (!out) { GSETERROR(err, "ZAPI error : no output"); LOG_RETURN(NULL,"Failure (zapi error)"); } if (na_results_status(out) != NA_OK) { na_elem_free(out); GSETERROR(err, "NetApp filer error : (%d) %s", na_results_errno(out), na_results_reason(out)); LOG_RETURN(NULL,"Failure (server)"); } /* Reply's content handling */ ht = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); instances = na_elem_child(out, "instances"); for (iter_i=na_child_iterator(instances); (instance = na_iterator_next(&iter_i)); ) { const char *iname; na_elem_t *counters, *counter; gint64 busy=1, busy_base=0; gint idle; iname = na_child_get_string(instance, "name"); counters = na_elem_child(instance, "counters"); for (iter_c=na_child_iterator(counters); (counter = na_iterator_next(&iter_c)) ; ) { const char *cname; gint i; cname = na_child_get_string(counter, "name"); i = na_child_get_int(counter, "value", 0); if (cname && *cname=='b' && 0==g_ascii_strcasecmp(cname,"base_for_disk_busy")) busy_base = i>0 ? i : 1; if (cname && *cname=='d' && 0==g_ascii_strcasecmp(cname,"disk_busy")) busy = i>0 ? i : 1; } busy_base /= busy; idle = 100LL - busy_base; g_hash_table_insert(ht, g_strdup(iname), GINT_TO_POINTER(idle)); XTRACE("Saved : disk[%s] -> idle[%d]", iname, idle); } na_elem_free(out); LOG_RETURN(ht,"Success"); }
/** * Read Input. */ char* read_input(void) { LOG_ENTRY; char* x = (char*)fgets(input_buffer, INPUT_BUFFER_SIZE, stdin); LOG_RETURN(x); }
static gboolean netapp_refresh_filer_data(struct filer_s *filer, GError **err) { gint64 in64, out64; gdouble in_idle, out_idle; GHashTable *ht_disk2idle; GList *l; GHashTableIter iter; GHashTable *ht_disk_counters; gpointer k, v; XTRACE("Entering"); if (!netapp_refresh_fixed_filer_data(filer, err)) { GSETERROR(err, "Uncomplete or too old filer data"); LOG_RETURN(FALSE, "Failure (uncomplete info)"); } /* get CPU-idle */ if (!snmp_get_template_int(filer->ctx->session, oid_cpuIdle, oid_cpuIdle_size, 0, &(filer->ctx->variable.cpu_idle), err)) { GSETERROR(err, "Failed to get network output on interface %d", filer->ctx->fixed.net_itf_index); LOG_RETURN(FALSE, "Failure (oid_ifNetOut)"); } XTRACE("cpuIdle = %"G_GINT64_FORMAT, filer->ctx->variable.cpu_idle); /* compute IO-idle */ if (!snmp_get_template_int(filer->ctx->session, oid_ifNetIn, oid_ifNetIn_size, filer->ctx->fixed.net_itf_index, &in64, err)) { GSETERROR(err, "Failed to get network input on interface %d", filer->ctx->fixed.net_itf_index); LOG_RETURN(FALSE, "Failure (oid_ifNetIn)"); } XTRACE("ifNetIn = %"G_GINT64_FORMAT, in64); if (!snmp_get_template_int(filer->ctx->session, oid_ifNetOut, oid_ifNetOut_size, filer->ctx->fixed.net_itf_index, &out64, err)) { GSETERROR(err, "Failed to get network output on interface %d", filer->ctx->fixed.net_itf_index); LOG_RETURN(FALSE, "Failure (oid_ifNetOut)"); } XTRACE("ifNetOut = %"G_GINT64_FORMAT, out64); if (filer->ctx->variable.net_in_last > in64) in_idle = 99.0; else { in_idle = in64 - filer->ctx->variable.net_in_last; in_idle /= 1.0 * filer->ctx->fixed.net_in_max; in_idle = 100.0 * (1.0 - in_idle); } if (filer->ctx->variable.net_out_last > out64) out_idle = 99.0; else { out_idle = out64 - filer->ctx->variable.net_out_last; out_idle /= 1.0 * filer->ctx->fixed.net_out_max; out_idle = 100.0 * (1.0 - out_idle); } filer->ctx->variable.net_idle = floor(MIN(in_idle,out_idle)); filer->ctx->variable.net_out_last = out64; filer->ctx->variable.net_in_last = in64; /* --------------------- */ /* Collect the disk-idle */ /* --------------------- */ if (!filer->ctx->variable.aggr2idle) filer->ctx->variable.aggr2idle = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); ht_disk2idle = zapi_get_disk2idle(filer->ctx->na_session, err); if (!ht_disk2idle) { GSETERROR(err,"Failed to collect the filer's disk-idle"); LOG_RETURN(FALSE,"Failure (Disk-idle)"); } ht_disk_counters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); /* reset the counters and accumulators */ for (l=filer->ctx->fixed.aggregates; l ; l=l->next) { g_hash_table_insert(ht_disk_counters, g_strdup(l->data), GINT_TO_POINTER(0)); g_hash_table_insert(filer->ctx->variable.aggr2idle, g_strdup(l->data), GINT_TO_POINTER(0)); } g_hash_table_iter_init(&iter, ht_disk2idle); while (g_hash_table_iter_next(&iter,&k,&v)) { gchar *aggr_name; aggr_name = g_hash_table_lookup(filer->ctx->fixed.disk2aggr, k); if (aggr_name) { gpointer p_count, p_sum; gint count, sum; p_count = g_hash_table_lookup(ht_disk_counters, aggr_name); p_sum = g_hash_table_lookup(filer->ctx->variable.aggr2idle, aggr_name); count = GPOINTER_TO_INT(p_count) + 1; sum = GPOINTER_TO_INT(p_sum) + GPOINTER_TO_INT(v); g_hash_table_insert(ht_disk_counters, aggr_name, GINT_TO_POINTER(count)); g_hash_table_insert(filer->ctx->variable.aggr2idle, g_strdup(aggr_name), GINT_TO_POINTER(sum)); XTRACE("aggr[%s] count[%d] sum[%d]", aggr_name, count, sum); } } for (l=filer->ctx->fixed.aggregates; l ; l=l->next) { gchar *aggr_name; gpointer p_count, p_sum; gint idle; aggr_name = l->data; p_count = g_hash_table_lookup(ht_disk_counters, aggr_name); p_sum = g_hash_table_lookup(filer->ctx->variable.aggr2idle, aggr_name); idle = 0; if (p_count && p_sum) idle = GPOINTER_TO_INT(p_sum) / GPOINTER_TO_INT(p_count); XTRACE("TOTAL : aggr[%s] sum[%d] count[%d] idle=[%d]", aggr_name, GPOINTER_TO_INT(p_sum), GPOINTER_TO_INT(p_count), idle); g_hash_table_insert(filer->ctx->variable.aggr2idle, g_strdup(aggr_name), GINT_TO_POINTER(idle)); } g_hash_table_destroy(ht_disk_counters); /* Well... everything seems to have heppened fine! */ LOG_RETURN(TRUE,"Success (idle=%"G_GINT64_FORMAT" out=%"G_GINT64_FORMAT" in=%"G_GINT64_FORMAT")", filer->ctx->variable.net_idle, out64, in64); }