+static const struct nbrec_dhcp_options *
+dhcp_options_get(struct ctl_context *ctx, const char *id, bool must_exist)
+{
+ struct uuid dhcp_opts_uuid;
+ const struct nbrec_dhcp_options *dhcp_opts = NULL;
+ if (uuid_from_string(&dhcp_opts_uuid, id)) {
+ dhcp_opts = nbrec_dhcp_options_get_for_uuid(ctx->idl, &dhcp_opts_uuid);
+ }
+
+ if (!dhcp_opts && must_exist) {
+ ctl_fatal("%s: dhcp options UUID not found", id);
+ }
+ return dhcp_opts;
+}
+
+static void
+nbctl_dhcp_options_create(struct ctl_context *ctx)
+{
+ /* Validate the cidr */
+ ovs_be32 ip;
+ unsigned int plen;
+ char *error = ip_parse_cidr(ctx->argv[1], &ip, &plen);
+ if (error){
+ /* check if its IPv6 cidr */
+ free(error);
+ struct in6_addr ipv6;
+ error = ipv6_parse_cidr(ctx->argv[1], &ipv6, &plen);
+ if (error) {
+ free(error);
+ ctl_fatal("Invalid cidr format '%s'", ctx->argv[1]);
+ return;
+ }
+ }
+
+ struct nbrec_dhcp_options *dhcp_opts = nbrec_dhcp_options_insert(ctx->txn);
+ nbrec_dhcp_options_set_cidr(dhcp_opts, ctx->argv[1]);
+
+ struct smap ext_ids = SMAP_INITIALIZER(&ext_ids);
+ for (size_t i = 2; i < ctx->argc; i++) {
+ char *key, *value;
+ value = xstrdup(ctx->argv[i]);
+ key = strsep(&value, "=");
+ if (value) {
+ smap_add(&ext_ids, key, value);
+ }
+ free(key);
+ }
+
+ nbrec_dhcp_options_set_external_ids(dhcp_opts, &ext_ids);
+ smap_destroy(&ext_ids);
+}
+
+static void
+nbctl_dhcp_options_set_options(struct ctl_context *ctx)
+{
+ const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
+ ctx, ctx->argv[1], true);
+
+ struct smap dhcp_options = SMAP_INITIALIZER(&dhcp_options);
+ for (size_t i = 2; i < ctx->argc; i++) {
+ char *key, *value;
+ value = xstrdup(ctx->argv[i]);
+ key = strsep(&value, "=");
+ if (value) {
+ smap_add(&dhcp_options, key, value);
+ }
+ free(key);
+ }
+
+ nbrec_dhcp_options_set_options(dhcp_opts, &dhcp_options);
+ smap_destroy(&dhcp_options);
+}
+
+static void
+nbctl_dhcp_options_get_options(struct ctl_context *ctx)
+{
+ const struct nbrec_dhcp_options *dhcp_opts = dhcp_options_get(
+ ctx, ctx->argv[1], true);
+
+ struct smap_node *node;
+ SMAP_FOR_EACH(node, &dhcp_opts->options) {
+ ds_put_format(&ctx->output, "%s=%s\n", node->key, node->value);
+ }
+}
+
+static void
+nbctl_dhcp_options_del(struct ctl_context *ctx)
+{
+ bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ const char *id = ctx->argv[1];
+ const struct nbrec_dhcp_options *dhcp_opts;
+
+ dhcp_opts = dhcp_options_get(ctx, id, must_exist);
+ if (!dhcp_opts) {
+ return;
+ }
+
+ nbrec_dhcp_options_delete(dhcp_opts);
+}
+
+static void
+nbctl_dhcp_options_list(struct ctl_context *ctx)
+{
+ const struct nbrec_dhcp_options *dhcp_opts;
+ struct smap dhcp_options;
+
+ smap_init(&dhcp_options);
+ NBREC_DHCP_OPTIONS_FOR_EACH(dhcp_opts, ctx->idl) {
+ smap_add_format(&dhcp_options, dhcp_opts->cidr, UUID_FMT,
+ UUID_ARGS(&dhcp_opts->header_.uuid));
+ }
+ const struct smap_node **nodes = smap_sort(&dhcp_options);
+ for (size_t i = 0; i < smap_count(&dhcp_options); i++) {
+ const struct smap_node *node = nodes[i];
+ ds_put_format(&ctx->output, "%s\n", node->value);
+ }
+ smap_destroy(&dhcp_options);
+ free(nodes);
+}
+