[Freeipa-devel] Structured DNS record API proposal

Jakub Hrozek jhrozek at redhat.com
Fri Sep 23 10:32:21 UTC 2011


On Thu, Sep 22, 2011 at 09:59:01PM -0400, Dmitri Pal wrote:
> On 09/22/2011 03:37 AM, Jakub Hrozek wrote:
> > On Thu, Sep 22, 2011 at 08:25:01AM +0200, Jan Cholasta wrote:
> >> On 21.9.2011 23:55, Dmitri Pal wrote:
> >>> On 09/21/2011 10:27 AM, Adam Young wrote:
> >>>> On 09/20/2011 11:11 AM, Martin Kosek wrote:
> >>>>> On Tue, 2011-09-20 at 10:02 -0400, Adam Young wrote:
> >>>>>> This discussion got me thinking, always a dangerous proposal:
> >>>>>>
> >>>>>> We are currently exposing record add with the lie  that  when you add a
> >>>>>> record, it has a type.  THe reality is that a record is just this big
> >>>>>> collection of multi value attributes, and each of those  is the "type"
> >>>>>> of the record.
> >>>>> The way I see it is that we have different types of Resource Records
> >>>>> with a (domain) name that can be shared.
> >>>>>
> >>>>>> If all of the 'records'  have the same idnsname, then they really fall
> >>>>>> under the same Record object in LDAP.
> >>>>> Yes.
> >>>>>
> >>>>>> What if we focuses on the attribtutes themselves, and add the type info
> >>>>>> there.
> >>>>> I thought we do this already.
> >>>>>
> >>>>>> Pie in the sky proposal.   Treat it as a starting point:
> >>>>>>
> >>>>>>   From the webui perspective
> >>>>>> dnsrecord-add   allows the case where it just has the the idnsname with
> >>>>>> no "records"
> >>>>>>
> >>>>>> dnsrecordattr-mod  takes record type specific values.
> >>>>>>
> >>>>>> To add a location entry:
> >>>>>>
> >>>>>> ipa dnsrecordattr-mod --append location --lat-deg=INT --lat-min=INT
> >>>>>> --lat-sec=FLOAT --lat-dir=ENUM --lon-deg=INT --lon-min=INT
> >>>>>> --lon-sec=FLOAT --lon-dir=ENUM --alt=FLOAT --h-precision=FLOAT
> >>>>>> --v-precision=FLOAT
> >>>>>>
> >>>>>>
> >>>>>> And to remove it
> >>>>>>
> >>>>>> ipa dnsrecordattr-mod --remove location --lat-deg=INT --lat-min=INT
> >>>>>> --lat-sec=FLOAT --lat-dir=ENUM --lon-deg=INT --lon-min=INT
> >>>>>> --lon-sec=FLOAT --lon-dir=ENUM --alt=FLOAT --h-precision=FLOAT
> >>>>>> --v-precision=FLOAT
> >>>>> So if user would want to remove a LOC record, he would have to pass all
> >>>>> these attributes to refer which attribute value to remove?
> >>>> I think that is the case anyway.  Since a DNS record is really just an
> >>>> multivalue attribute,   you would now have to do  a dns-record-mod
> >>>> with the list of all LOC records that you don't want to delete.  I
> >>>> used this as an example because it is the most complex case.
> >>>>
> >>>> Just thinking it through...I'm not certain I like the "one command per
> >>>> record type"  as it changes a lot of other assumptions.  DNS is a
> >>>> wierd beast already.
> >>>>
> >>>> I've spent a lot of time on the DNS ui, and it is pretty tricky  to
> >>>> get right.  I'm trying to balance the PI against efficient usage.
> >>>>
> >>>> What we really need for the fields is a way to specify the format for
> >>>> a given field, much like the format strings used for group names.  For
> >>>> example, the LOC  record  is really
> >>>>
> >>>> <owner>   <TTL>   <class>   LOC d1 [m1 [s1]] {"N"|"S"}  d2 [m2 [s2]]
> >>>> {"E"|"W"}
> >>>>                           alt["m"] [siz["m"]     [hp["m"] [vp["m"]]]]
> >>>>
> >>>>
> >>>> And all the WebUI needs is a way to specify that format  to validate.
> >>>>
> >>> Can we use augeas for this?
> >>> Augeas lenses use this kind of the validation and there is python
> >>> binding so may be we should use augeas as an inspiration or ask for an
> >>> augeas Javascript solution?
> >> We can't. Augeas knows how to manipulate config files and only that,
> >> there is no API for anything else.
> >>
> > Some time ago I wrote a patch to extend Augeas to operate on arbitrary
> > strings. I never had time to push it upstream, but I think I still have
> > is somewhere.
> >
> > Not sure if this approach would help us anyway, we would still have to
> > wait until this feature made it to RHEL and solve the JS bindings as
> > well
> >
> 
> Yes I was thinking about this path at least as an inspiration.
> 

OK, I've dug up and attached the patch.

Obviously it doesn't apply upstream anymore, but it is still a very good
starting point if someone wants to tackle this problem.
-------------- next part --------------
>From 191110b81376f831b211e34f491a379d69c79cb0 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 25 Mar 2009 17:04:54 +0100
Subject: [PATCH] aug_lens_get

---
 src/augeas.c           |   29 +++++++++++
 src/augeas.h           |   20 ++++++++
 src/augeas_sym.version |    1 +
 src/augtool.c          |   16 ++++++
 src/internal.h         |    8 +++
 src/transform.c        |  121 +++++++++++++++++++++++++++++++++++++----------
 src/transform.h        |    4 ++
 7 files changed, 173 insertions(+), 26 deletions(-)

diff --git a/src/augeas.c b/src/augeas.c
index 192a79a..291906e 100644
--- a/src/augeas.c
+++ b/src/augeas.c
@@ -978,6 +978,35 @@ int aug_print(const struct augeas *aug, FILE *out, const char *pathin) {
     return result;
 }
 
+int aug_lens_get(augeas *aug, const char *path, const char *lens,
+                 const char *txt, size_t txt_len) {
+    char *text = NULL;
+    int result = -1;
+
+    /* append newline to the end of the string if needed */
+    if (txt_len == 0 || txt[txt_len-1] != '\n') {
+        if (ALLOC_N(text, txt_len+2) < 0) {
+            goto fini;
+        }
+        strcpy(text, txt);
+        text[txt_len]   = '\n';
+        text[txt_len+1] = '\0';
+        txt_len += 2;
+    } else {
+        text = (char *) txt;
+    }
+
+    result = transform_string(aug, lens, path, text, txt_len);
+    if (result != 0) {
+        goto fini;
+    }
+
+    result = 0;
+fini:
+    if (text != txt) free(text);
+    return result;
+}
+
 void aug_close(struct augeas *aug) {
     if (aug == NULL)
         return;
diff --git a/src/augeas.h b/src/augeas.h
index 9792bad..435815b 100644
--- a/src/augeas.h
+++ b/src/augeas.h
@@ -250,6 +250,26 @@ int aug_save(augeas *aug);
  */
 int aug_load(augeas *aug);
 
+
+/* Function: aug_lens_get
+ *
+ * Transform the string TXT into a tree using the get direction of lens
+ * LENS and put the resulting tree into the tree in AUG at PATH.
+ *
+ * The PATH must not start with '/augeas', and should not start with
+ * '/files', unless you know very well what you are doing.
+ *
+ * The LENS is the fully qualified name of a lens in the form
+ * 'Module.lens'.
+ *
+ * The string TXT is a nul-terminated string of length TXT_LEN
+ *
+ * Return -1 on error, and 0 on success
+ */
+
+int aug_lens_get(augeas *aug, const char *path, const char *lens,
+                 const char *txt, size_t txt_len);
+
 /* Function: aug_print
  *
  * Print each node matching PATH and its descendants to OUT.
diff --git a/src/augeas_sym.version b/src/augeas_sym.version
index 0643904..2236967 100644
--- a/src/augeas_sym.version
+++ b/src/augeas_sym.version
@@ -10,6 +10,7 @@ AUGEAS_0.1.0 {
       aug_match;
       aug_save;
       aug_print;
+      aug_lens_get;
       # Symbols with __ are private
       __aug_load_module_file;
     local: *;
diff --git a/src/augtool.c b/src/augtool.c
index c1ac383..b5ab106 100644
--- a/src/augtool.c
+++ b/src/augtool.c
@@ -251,6 +251,19 @@ static int cmd_get(char *args[]) {
     return 0;
 }
 
+static int cmd_lens_get(char *args[]) {
+    const char *path = cleanpath(args[0]);
+    const char *module = cleanpath(args[1]);
+    const char *txt = cleanpath(args[2]);
+    int ret;
+
+    ret = aug_lens_get(aug, path, module, txt, strlen(txt));
+    if(ret != 0)
+        printf ("Failed\n");
+
+    return ret;
+}
+
 static int cmd_print(char *args[]) {
     return aug_print(aug, stdout, cleanpath(args[0]));
 }
@@ -421,6 +434,9 @@ static const struct command const commands[] = {
       "Save all pending changes to disk. For now, files are not overwritten.\n"
       "        Instead, new files with extension .augnew are created"
     },
+    { "lens_get", 3, 3, cmd_lens_get, "lens_get <PATH> <LENS> <TEXT>",
+      "Parse text according to LENS and store result at PATH/\n"
+    },
     { "load", 0, 0, cmd_load, "load",
       "Load files accordig to the transforms in /augeas/load."
     },
diff --git a/src/internal.h b/src/internal.h
index cc636ef..7992fa7 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -63,6 +63,14 @@
  * Information about files */
 #define AUGEAS_META_FILES AUGEAS_META_TREE AUGEAS_FILES_TREE
 
+/* Define: AUGEAS_TEXT_TREE
+ * A hierarchy where we keep directly parsed text */
+#define AUGEAS_TEXT_TREE "/text"
+
+/* Degine: AUGEAS_META_TEXT
+ * Information about directly parsed text */
+#define AUGEAS_META_TEXT AUGEAS_META_TREE AUGEAS_TEXT_TREE
+
 /* Define: AUGEAS_META_ROOT
  * The root directory */
 #define AUGEAS_META_ROOT AUGEAS_META_TREE "/root"
diff --git a/src/transform.c b/src/transform.c
index b50a9bb..701f3ed 100644
--- a/src/transform.c
+++ b/src/transform.c
@@ -201,12 +201,12 @@ void free_transform(struct transform *xform) {
     free(xform);
 }
 
-static char *err_path(const char *filename) {
+static char *err_path(const char *filename, const char *base) {
     char *result = NULL;
     if (filename == NULL)
-        pathjoin(&result, 2, AUGEAS_META_FILES, s_error);
+        pathjoin(&result, 2, base, s_error);
     else
-        pathjoin(&result, 3, AUGEAS_META_FILES, filename, s_error);
+        pathjoin(&result, 3, base, filename, s_error);
     return result;
 }
 
@@ -252,58 +252,83 @@ calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs)
 }
 
 /* Record an error in the tree. The error will show up underneath
- * /augeas/FILENAME/error. PATH is the path to the toplevel node in the
+ * ERROR_NODE. PATH is the path to the toplevel node in the
  * tree where the lens application happened. When STATUS is NULL, just
- * clear any error associated with FILENAME in the tree.
+ * clear any error associated with ERROR_NODE in the tree.
  */
 static int store_error(struct augeas *aug,
-                       const char *filename, const char *path,
+                       char **error_node, const char *path,
                        const char *status, int errnum,
                        const struct lns_error *err, const char *text) {
-    char *ep = err_path(filename);
     int r;
-    int result = -1;
-
-    if (ep == NULL)
-        return -1;
 
-    aug_rm(aug, ep);
+    aug_rm(aug, *error_node);
     if (status != NULL) {
-        r = aug_set(aug, ep, status);
+        r = aug_set(aug, *error_node, status);
         if (r < 0)
-            goto done;
+            return -1;
 
         /* Errors from err_set are ignored on purpose. We try
          * to report as much as we can */
         if (err != NULL) {
             if (err->pos >= 0) {
                 size_t line, ofs;
-                err_set(aug, &ep, s_pos, "%d", err->pos);
+                err_set(aug, error_node, s_pos, "%d", err->pos);
                 if (text != NULL) {
                     calc_line_ofs(text, err->pos, &line, &ofs);
-                    err_set(aug, &ep, s_line, "%zd", line);
-                    err_set(aug, &ep, s_char, "%zd", ofs);
+                    err_set(aug, error_node, s_line, "%zd", line);
+                    err_set(aug, error_node, s_char, "%zd", ofs);
                 }
             }
             if (err->path != NULL) {
-                err_set(aug, &ep, s_path, "%s%s", path, err->path);
+                err_set(aug, error_node, s_path, "%s%s", path, err->path);
             }
             if (err->lens != NULL) {
                 char *fi = format_info(err->lens->info);
                 if (fi != NULL) {
-                    err_set(aug, &ep, s_lens, "%s", fi);
+                    err_set(aug, error_node, s_lens, "%s", fi);
                     free(fi);
                 }
             }
-            err_set(aug, &ep, s_message, "%s", err->message);
+            err_set(aug, error_node, s_message, "%s", err->message);
         } else if (errnum != 0) {
             const char *msg = strerror(errnum);
-            err_set(aug, &ep, s_message, "%s", msg);
+            err_set(aug, error_node, s_message, "%s", msg);
         }
     }
 
-    result = 0;
- done:
+}
+
+/* Store an error associated to FILENAME under /augeas/FILENAME/error
+ */
+static int store_error_file(struct augeas *aug,
+                            const char *filename, const char *path,
+                            const char *status, int errnum,
+                            const struct lns_error *err, const char *text) {
+    char *ep = err_path(filename, AUGEAS_META_FILES);
+    int result = -1;
+
+    if (ep == NULL)
+        return -1;
+
+    result = store_error(aug, &ep, path, status, errnum, err, text);
+
+    free(ep);
+    return result;
+}
+
+static int store_error_string(struct augeas *aug,
+                              const char *string_name, const char *path,
+                              const char *status, int errnum,
+                              const struct lns_error *err, const char *text) {
+    char *ep = err_path(string_name, AUGEAS_META_TEXT);
+    int result = -1;
+
+    if (ep == NULL)
+        return -1;
+
+    result = store_error(aug, &ep, path, status, errnum, err, text);
+
     free(ep);
     return result;
 }
@@ -417,7 +442,7 @@ static int load_file(struct augeas *aug, struct lens *lens, char *filename) {
 
     result = 0;
  done:
-    store_error(aug, filename + strlen(aug->root) - 1, path, err_status,
+    store_error_file(aug, filename + strlen(aug->root) - 1, path, err_status,
                 errno, err, text);
     free_lns_error(err);
     free(path);
@@ -847,7 +872,7 @@ int transform_save(struct augeas *aug, struct tree *xfm,
     {
         const char *emsg =
             dyn_err_status == NULL ? err_status : dyn_err_status;
-        store_error(aug, filename, path, emsg, errno, err, NULL);
+        store_error_file(aug, filename, path, emsg, errno, err, NULL);
     }
     free(dyn_err_status);
     lens_release(lens);
@@ -935,7 +960,7 @@ int remove_file(struct augeas *aug, struct tree *tree) {
     {
         const char *emsg =
             dyn_err_status == NULL ? err_status : dyn_err_status;
-        store_error(aug, filename, path, emsg, errno, NULL, NULL);
+        store_error_file(aug, filename, path, emsg, errno, NULL, NULL);
     }
     free(path);
     free(augorig);
@@ -945,6 +970,50 @@ int remove_file(struct augeas *aug, struct tree *tree) {
     return -1;
 }
 
+int transform_string(struct augeas *aug, const char *lens_path, const char *basepath, 
+                     const char *txt, size_t txt_len) {
+    struct info *info = NULL;
+    struct lns_error *err = NULL;
+    struct tree *tree = NULL;
+    int result = -1;
+    const char *err_status = NULL;
+    struct lens *lens = NULL;
+    char *path = NULL;
+
+    pathjoin(&path, 2, AUGEAS_TEXT_TREE, basepath);
+
+    lens = lens_from_name(aug, lens_path);
+    if(lens == NULL) {
+        goto done;
+    }
+
+    make_ref(info);
+    info->first_line = 1;
+    info->last_line = 1;
+    info->first_column = 1;
+    info->last_column = txt_len;
+
+    tree = lns_get(info, lens, txt, &err);
+    if (err != NULL) {
+        err_status = "parse_failed";
+        goto done;
+    }
+
+    unref(info, info);
+
+    tree_replace(aug->origin, path, tree);
+    tree = NULL;
+
+    result = 0;
+done:
+    store_error_string(aug, basepath, path, err_status,
+                errno, err, txt);
+    free(path);
+    free_tree(tree);
+    free_lns_error(err);
+    return result;
+}
+
 /*
  * Local variables:
  *  indent-tabs-mode: nil
diff --git a/src/transform.h b/src/transform.h
index 1178f64..d6fb841 100644
--- a/src/transform.h
+++ b/src/transform.h
@@ -87,6 +87,10 @@ int transform_save(struct augeas *aug, struct tree *xfm,
  * Return 0 on success, -1 on failure
  */
 int remove_file(struct augeas *aug, struct tree *tree);
+
+int transform_string(struct augeas *aug, const char *lens, const char *basepath, 
+                     const char *txt, size_t txt_len);
+
 #endif
 
 
-- 
1.7.6.2



More information about the Freeipa-devel mailing list