On Fri, Jul 18, 2025 at 6:15 PM Mark Michelson via dev <
ovs-dev@openvswitch.org> wrote:

> Prior to this commit, if you wanted to find the corresponding northbound
> UUID for a southbound Logical Datapath, you could find it in one of two
> places:
>
> For logical switches, it was in external-ids:logical-switch.
> For logical routers, it was in external-ids:logical-router.
>
> With this commit, we are separating the type and UUID into separate
> fields. This way, no matter the type of the datapath, you can find the
> UUID. And you can find the type of the datapath without having to
> potentially check multiple external-ids keys to do so.
>
> These fields are going to be used pretty heavily by northd in upcoming
> patches, so instead of making them external-ids, these are now
> full-fledged columns on the southbound Datapath_Binding. The UUID of the
> northbound logical datapath is in a column called "nb_uuid" and the type
> of the logical datapath is stored in an enumerated column called "type".
>
> Note that there is a seemingly-unrelated change in the check packet
> length test in tests/ovn.at. This test started failing because its use
> of `grep` was picking up the new "nb_uuid" column accidentally. The
> change to the test uses `fetch_column` since it is more precise.
>
> Signed-off-by: Mark Michelson <mmich...@redhat.com>
> ---
> v11
>  * Rebased.
>
> v10
>  * Rebased.
>  * Maintained backwards-compatibility with external_ids method of
>    storing NB UUID and type. It was recommended to continue setting
>    the external-ids in addition to the new fields. This patch does not
>    do that because that code would get immediately wiped out by the
>    next patch in the series. The next patch in the series does write
>    the old external_ids values, though.
>
> v9
>  * Rebased.
>
> v8
>  * Rebased.
>  * Changed the types of the new columns. The nb_uuid is type "uuid" and
>    the type is an enum.
>
> v7
>  * Rebased.
>  * Changed from using external-ids for the nb_uuid and type to using
>    new columns in the Datapath_Binding table.
>
> v6
>  * This is the first version of the series to contain this patch.
> ---
>  controller/local_data.c |  2 +-
>  ic/ovn-ic.c             |  5 +++--
>  lib/ovn-util.c          | 45 +++++++++++++++++++++++++++++++++++++++++
>  lib/ovn-util.h          |  8 ++++++++
>  northd/northd.c         | 38 +++++++++++++++++-----------------
>  northd/northd.h         |  5 +++--
>  ovn-sb.ovsschema        | 11 ++++++++--
>  ovn-sb.xml              | 21 ++++++++++---------
>  tests/ovn-controller.at |  4 ++++
>  tests/ovn-northd.at     |  6 +++---
>  tests/ovn.at            |  6 ++----
>  utilities/ovn-sbctl.c   |  4 ++--
>  utilities/ovn-trace.c   |  3 +--
>  13 files changed, 112 insertions(+), 46 deletions(-)
>
> diff --git a/controller/local_data.c b/controller/local_data.c
> index 00383d091..a35d3fa5a 100644
> --- a/controller/local_data.c
> +++ b/controller/local_data.c
> @@ -708,7 +708,7 @@ local_datapath_peer_port_add(struct local_datapath *ld,
>  static bool
>  datapath_is_switch(const struct sbrec_datapath_binding *ldp)
>  {
> -    return smap_get(&ldp->external_ids, "logical-switch") != NULL;
> +    return strcmp(datapath_get_nb_type(ldp), "logical-switch") == 0;
>  }
>
>  static bool
> diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
> index caffa6fe0..0cd78e720 100644
> --- a/ic/ovn-ic.c
> +++ b/ic/ovn-ic.c
> @@ -613,8 +613,7 @@ get_router_uuid_by_sb_pb(struct ic_context *ctx,
>          return NULL;
>      }
>
> -    return smap_get_uuid(&router_pb->datapath->external_ids,
> "logical-router",
> -                         router_uuid);
> +    return datapath_get_nb_uuid(router_pb->datapath, router_uuid);
>  }
>
>  static void
> @@ -2582,6 +2581,8 @@ main(int argc, char *argv[])
>      ovsdb_idl_add_table(ovnsb_idl_loop.idl,
> &sbrec_table_datapath_binding);
>      ovsdb_idl_add_column(ovnsb_idl_loop.idl,
>                           &sbrec_datapath_binding_col_external_ids);
> +    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
> +                         &sbrec_datapath_binding_col_nb_uuid);
>
>      ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
>      ovsdb_idl_add_column(ovnsb_idl_loop.idl,
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index 9f0bc99ba..abfff0890 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -1484,3 +1484,48 @@ ovn_mirror_port_name(const char *datapath_name,
>  {
>      return xasprintf("mp-%s-%s", datapath_name, port_name);
>  }
> +
> +bool
> +datapath_get_nb_uuid_and_type(const struct sbrec_datapath_binding *sb,
> +                              struct uuid *nb_uuid, const char **type)
> +{
> +    if (sb->nb_uuid) {
> +        /* New style. The UUID and type are direct columns, so use those.
> */
> +        *nb_uuid = *sb->nb_uuid;
> +        *type = sb->type;
> +        return true;
> +    }
> +
> +    /* Old style. The UUID is stored in external_ids, and the key
> +     * corresponds to the datapath type. This only works with
> +     * logical switches and logical routers.
> +     */
> +    *type = "logical-switch";
> +    if (smap_get_uuid(&sb->external_ids, *type, nb_uuid)) {
> +        return true;
> +    }
> +    *type = "logical-router";
> +    if (smap_get_uuid(&sb->external_ids, *type, nb_uuid)) {
> +        return true;
> +    }
> +    *type = "";
> +    *nb_uuid = UUID_ZERO;
> +    return false;
> +}
> +
> +bool
> +datapath_get_nb_uuid(const struct sbrec_datapath_binding *sb,
> +                     struct uuid *nb_uuid)
> +{
> +    const char *type;
> +    return datapath_get_nb_uuid_and_type(sb, nb_uuid, &type);
> +}
> +
> +const char *
> +datapath_get_nb_type(const struct sbrec_datapath_binding *sb)
> +{
> +    const char *type;
> +    struct uuid nb_uuid;
> +    datapath_get_nb_uuid_and_type(sb, &nb_uuid, &type);
> +    return type;
> +}
> diff --git a/lib/ovn-util.h b/lib/ovn-util.h
> index 2468a6de4..695058f90 100644
> --- a/lib/ovn-util.h
> +++ b/lib/ovn-util.h
> @@ -540,4 +540,12 @@ const struct sbrec_port_binding *lport_lookup_by_name(
>  #define _VFUNC(name, n) _VFUNC_(name, n)
>  #define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)
>
> +bool datapath_get_nb_uuid_and_type(const struct sbrec_datapath_binding
> *sb,
> +                                   struct uuid *nb_uuid, const char
> **type);
> +
> +bool datapath_get_nb_uuid(const struct sbrec_datapath_binding *sb,
> +                          struct uuid *nb_uuid);
> +
> +const char *datapath_get_nb_type(const struct sbrec_datapath_binding *sb);
> +
>  #endif /* OVN_UTIL_H */
> diff --git a/northd/northd.c b/northd/northd.c
> index b8ca445bc..f39ff77ca 100644
> --- a/northd/northd.c
> +++ b/northd/northd.c
> @@ -577,19 +577,21 @@ ovn_datapath_from_sbrec(const struct hmap
> *ls_datapaths,
>                          const struct hmap *lr_datapaths,
>                          const struct sbrec_datapath_binding *sb)
>  {
> -    struct uuid key;
>      const struct hmap *dps;
> +    struct uuid key;
> +    const char *type;
> +    if (!datapath_get_nb_uuid_and_type(sb, &key, &type)) {
> +        return NULL;
> +    }
>
> -    if (smap_get_uuid(&sb->external_ids, "logical-switch", &key)) {
> +    if (!strcmp(type, "logical-switch")) {
>          dps = ls_datapaths;
> -    } else if (smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
> +    } else if (!strcmp(type, "logical-router")) {
>          dps = lr_datapaths;
>      } else {
>          return NULL;
>      }
> -    if (!dps) {
> -        return NULL;
> -    }
> +
>      struct ovn_datapath *od = ovn_datapath_find_(dps, &key);
>      if (od && (od->sb == sb)) {
>          return od;
> @@ -749,11 +751,9 @@ store_mcast_info_for_switch_datapath(const struct
> sbrec_ip_multicast *sb,
>  static void
>  ovn_datapath_update_external_ids(struct ovn_datapath *od)
>  {
> -    /* Get the logical-switch or logical-router UUID to set in
> -     * external-ids. */
> +    /* Get the NB  UUID to set in external-ids. */
>      char uuid_s[UUID_LEN + 1];
>      sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
> -    const char *key = od->nbs ? "logical-switch" : "logical-router";
>
>      /* Get names to set in external-ids. */
>      const char *name = od->nbs ? od->nbs->name : od->nbr->name;
> @@ -765,7 +765,6 @@ ovn_datapath_update_external_ids(struct ovn_datapath
> *od)
>
>      /* Set external-ids. */
>      struct smap ids = SMAP_INITIALIZER(&ids);
> -    smap_add(&ids, key, uuid_s);
>      smap_add(&ids, "name", name);
>      if (name2 && name2[0]) {
>          smap_add(&ids, "name2", name2);
> @@ -893,13 +892,10 @@ join_datapaths(const struct
> nbrec_logical_switch_table *nbrec_ls_table,
>      const struct sbrec_datapath_binding *sb;
>      SBREC_DATAPATH_BINDING_TABLE_FOR_EACH_SAFE (sb, sbrec_dp_table) {
>          struct uuid key;
> -        if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
> -            !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
> +        if (!datapath_get_nb_uuid(sb, &key)) {
>              ovsdb_idl_txn_add_comment(
>                  ovnsb_txn,
> -                "deleting Datapath_Binding "UUID_FMT" that lacks "
> -                "external-ids:logical-switch and "
> -                "external-ids:logical-router",
> +                "deleting Datapath_Binding "UUID_FMT" that lacks nb_uuid",
>                  UUID_ARGS(&sb->header_.uuid));
>              sbrec_datapath_binding_delete(sb);
>              continue;
> @@ -909,13 +905,13 @@ join_datapaths(const struct
> nbrec_logical_switch_table *nbrec_ls_table,
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
>              VLOG_INFO_RL(
>                  &rl, "deleting Datapath_Binding "UUID_FMT" with "
> -                "duplicate external-ids:logical-switch/router "UUID_FMT,
> -                UUID_ARGS(&sb->header_.uuid), UUID_ARGS(&key));
> +                "duplicate nb_uuid "UUID_FMT,
> +                UUID_ARGS(&sb->header_.uuid), UUID_ARGS(sb->nb_uuid));
>              sbrec_datapath_binding_delete(sb);
>              continue;
>          }
>
> -        struct ovn_datapath *od = ovn_datapath_create(datapaths, &key,
> +        struct ovn_datapath *od = ovn_datapath_create(datapaths,
> sb->nb_uuid,
>                                                        NULL, NULL, sb);
>          ovs_list_push_back(sb_only, &od->list);
>      }
> @@ -1117,11 +1113,17 @@ build_datapaths(struct ovsdb_idl_txn *ovnsb_txn,
>              sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
>          }
>          ovn_datapath_update_external_ids(od);
> +        sbrec_datapath_binding_set_nb_uuid(od->sb, &od->key, 1);
> +        sbrec_datapath_binding_set_type(od->sb, od->nbs ?
> "logical-switch" :
> +                                        "logical-router");
>      }
>      LIST_FOR_EACH (od, list, &nb_only) {
>          od->sb = sbrec_datapath_binding_insert(ovnsb_txn);
>          ovn_datapath_update_external_ids(od);
>          sbrec_datapath_binding_set_tunnel_key(od->sb, od->tunnel_key);
> +        sbrec_datapath_binding_set_nb_uuid(od->sb, &od->key, 1);
> +        sbrec_datapath_binding_set_type(od->sb, od->nbs ?
> "logical-switch" :
> +                                        "logical-router");
>      }
>      ovn_destroy_tnlids(&dp_tnlids);
>
> diff --git a/northd/northd.h b/northd/northd.h
> index eacff73eb..2b2fedec5 100644
> --- a/northd/northd.h
> +++ b/northd/northd.h
> @@ -347,7 +347,7 @@ DRR_MODES
>  #undef DRR_MODE
>
>  /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
> - * sb->external_ids:logical-switch. */
> + * sb->nb_uuid. */
>  struct ovn_datapath {
>      struct hmap_node key_node;  /* Index on 'key'. */
>      struct uuid key;            /* (nbs/nbr)->header_.uuid. */
> @@ -449,7 +449,8 @@ ovn_datapath_is_stale(const struct ovn_datapath *od)
>  /* The two purposes for which ovn-northd uses OVN logical datapaths. */
>  enum ovn_datapath_type {
>      DP_SWITCH,                  /* OVN logical switch. */
> -    DP_ROUTER                   /* OVN logical router. */
> +    DP_ROUTER,                  /* OVN logical router. */
> +    DP_MAX,
>  };
>
>  /* Returns an "enum ovn_stage" built from the arguments.
> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
> index 4c24f5b51..8bc7bbba3 100644
> --- a/ovn-sb.ovsschema
> +++ b/ovn-sb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Southbound",
> -    "version": "21.2.0",
> -    "cksum": "29145795 34859",
> +    "version": "21.3.0",
> +    "cksum": "3179330761 35286",
>      "tables": {
>          "SB_Global": {
>              "columns": {
> @@ -196,6 +196,13 @@
>                  "load_balancers": {"type": {"key": {"type": "uuid"},
>                                              "min": 0,
>                                              "max": "unlimited"}},
> +                "type": {"type": {"key": {"type": "string",
> +                                          "enum": ["set",
> +                                                      ["logical-switch",
> +
>  "logical-router"]]}}},
> +                "nb_uuid": {"type": {"key": {"type": "uuid"},
> +                                     "min": 0,
> +                                     "max": 1}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> diff --git a/ovn-sb.xml b/ovn-sb.xml
> index db5faac66..ad09a472e 100644
> --- a/ovn-sb.xml
> +++ b/ovn-sb.xml
> @@ -3236,18 +3236,19 @@ tcp.flags = RST;
>          db="OVN_Northbound"/> database.
>        </p>
>
> -      <column name="external_ids" key="logical-switch" type='{"type":
> "uuid"}'>
> -        For a logical datapath that represents a logical switch,
> -        <code>ovn-northd</code> stores in this key the UUID of the
> -        corresponding <ref table="Logical_Switch" db="OVN_Northbound"/>
> row in
> -        the <ref db="OVN_Northbound"/> database.
> +      <column name="type" type='{"type": "string"}'>
> +        This represents the type of the northbound logical datapath that
> is
> +        represented by this southbound <code>Datapath_Binding</code>. The
> +        possible values for this are:
> +        <ul>
> +          <li><code>logical-switch</code></li>
> +          <li><code>logical-router</code></li>
> +        </ul>
>        </column>
>
> -      <column name="external_ids" key="logical-router" type='{"type":
> "uuid"}'>
> -        For a logical datapath that represents a logical router,
> -        <code>ovn-northd</code> stores in this key the UUID of the
> -        corresponding <ref table="Logical_Router" db="OVN_Northbound"/>
> row in
> -        the <ref db="OVN_Northbound"/> database.
> +      <column name="nb_uuid" type='{"type": "string"}'>
> +        This is the UUID of the corresponding northbound logical datapath
> +        represented by this southbound <code>Datapath_Binding</code>.
>        </column>
>
>        <column name="external_ids" key="interconn-ts" type='{"type":
> "string"}'>
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index 6007a7454..4e73dcdcf 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -123,6 +123,7 @@ check_patches
>  # another logical port bound to this chassis.
>  check_uuid ovn-sbctl \
>      -- --id=@dp101 create Datapath_Binding tunnel_key=101
> external_ids:name=dp101 \
> +       type=logical-switch \
>      -- create Port_Binding datapath=@dp101 logical_port=localnet1
> tunnel_key=1 \
>          type=localnet options:network_name=physnet1
>  check_patches
> @@ -131,6 +132,7 @@ check_patches
>  # Now we should get some patch ports created.
>  check_uuid ovn-sbctl \
>      -- --id=@dp102 create Datapath_Binding tunnel_key=102
> external_ids:name=dp102 \
> +       type=logical-switch \
>      -- create Port_Binding datapath=@dp102 logical_port=localnet2
> tunnel_key=1 \
>          type=localnet options:network_name=physnet1 \
>      -- create Port_Binding datapath=@dp102 logical_port=localvif2
> tunnel_key=2
> @@ -145,7 +147,9 @@ check_patches \
>  # the set of OVS patch ports doesn't change.
>  AT_CHECK([ovn-sbctl \
>      -- --id=@dp1 create Datapath_Binding tunnel_key=1
> external_ids:name=dp1 \
> +       type=logical-switch \
>      -- --id=@dp2 create Datapath_Binding tunnel_key=2
> external_ids:name=dp2 \
> +       type=logical-switch \
>      -- create Port_Binding datapath=@dp1 logical_port=foo tunnel_key=1
> type=patch options:peer=bar \
>      -- create Port_Binding datapath=@dp2 logical_port=bar tunnel_key=2
> type=patch options:peer=foo \
>      -- create Port_Binding datapath=@dp1 logical_port=dp1vif tunnel_key=3
> \
> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
> index 8204a5cc3..58d8e66e9 100644
> --- a/tests/ovn-northd.at
> +++ b/tests/ovn-northd.at
> @@ -1893,8 +1893,8 @@ check ovn-nbctl --wait=sb \
>
>  check_row_count Datapath_Binding 1
>
> -nb_uuid=$(ovn-sbctl get Datapath_Binding . external_ids:logical-router)
> -lr_uuid=\"$(ovn-nbctl get Logical_Router . _uuid)\"
> +nb_uuid=$(ovn-sbctl get Datapath_Binding . nb_uuid)
> +lr_uuid=$(ovn-nbctl get Logical_Router . _uuid)
>  echo nb_uuid="$nb_uuid" lr_uuid="$lr_uuid"
>  AT_CHECK([test "${nb_uuid}" = "${lr_uuid}"])
>
> @@ -7582,7 +7582,7 @@ ls1_uuid=$(fetch_column nb:Logical_Switch _uuid)
>
>  # create a duplicated sb datapath (and an IP_Mulicast record that
> references
>  # it) on purpose.
> -AT_CHECK([ovn-sbctl --id=@dp create Datapath_Binding
> external_ids:logical-switch=$ls1_uuid external_ids:name=ls1 tunnel_key=123
> -- create IP_Multicast datapath=@dp], [0], [ignore])
> +AT_CHECK([ovn-sbctl --id=@dp create Datapath_Binding nb_uuid=$ls1_uuid
> type=logical-switch external_ids:name=ls1 tunnel_key=123 -- create
> IP_Multicast datapath=@dp], [0], [ignore])
>
>  # northd should delete one of the datapaths in the end
>  wait_row_count Datapath_Binding 1
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 108de260e..a6c7ae359 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -22194,8 +22194,7 @@ AT_CAPTURE_FILE([sbflows])
>  AT_CHECK([as hv1 ovs-ofctl dump-flows br-int  \
>  | grep "check_pkt_larger" | wc -l], [0], [[0
>  ]])
> -dp_uuid=$(ovn-sbctl find datapath_binding | grep lr0 -B2 | grep _uuid | \
> -awk '{print $3}')
> +dp_uuid=$(fetch_column Datapath_Binding _uuid external-ids:name=lr0)
>  check_uuid ovn-sbctl create MAC_Binding ip=172.168.0.3 datapath=$dp_uuid \
>  logical_port=lr0-public mac="00\:00\:00\:12\:af\:11"
>
> @@ -22265,8 +22264,7 @@ check ovn-nbctl lr-nat-add lr1 snat 172.168.0.100
> 10.0.0.0/24
>  check ovn-nbctl lr-nat-add lr1 snat 2000::1 1000::/64
>  check ovn-nbctl --wait=sb sync
>
> -dp_uuid=$(ovn-sbctl find datapath_binding | grep lr1 -B2 | grep _uuid | \
> -awk '{print $3}')
> +dp_uuid=$(fetch_column Datapath_Binding _uuid external-ids:name=lr1)
>  check_uuid ovn-sbctl create MAC_Binding ip=172.168.0.3 datapath=$dp_uuid \
>  logical_port=lr1-public mac="00\:00\:00\:12\:af\:11"
>
> diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c
> index 60cc39149..74f759699 100644
> --- a/utilities/ovn-sbctl.c
> +++ b/utilities/ovn-sbctl.c
> @@ -382,6 +382,7 @@ pre_get_info(struct ctl_context *ctx)
>      ovsdb_idl_add_column(ctx->idl, &sbrec_logical_dp_group_col_datapaths);
>
>      ovsdb_idl_add_column(ctx->idl,
> &sbrec_datapath_binding_col_external_ids);
> +    ovsdb_idl_add_column(ctx->idl, &sbrec_datapath_binding_col_nb_uuid);
>
>      ovsdb_idl_add_column(ctx->idl, &sbrec_ip_multicast_col_datapath);
>      ovsdb_idl_add_column(ctx->idl, &sbrec_ip_multicast_col_seq_no);
> @@ -1493,8 +1494,7 @@ static const struct ctl_table_class
> tables[SBREC_N_TABLES] = {
>      [SBREC_TABLE_DATAPATH_BINDING].row_ids
>       = {{&sbrec_datapath_binding_col_external_ids, "name", NULL},
>          {&sbrec_datapath_binding_col_external_ids, "name2", NULL},
> -        {&sbrec_datapath_binding_col_external_ids, "logical-switch",
> NULL},
> -        {&sbrec_datapath_binding_col_external_ids, "logical-router",
> NULL}},
> +        {&sbrec_datapath_binding_col_nb_uuid, NULL, NULL}},
>
>      [SBREC_TABLE_PORT_BINDING].row_ids
>       = {{&sbrec_port_binding_col_logical_port, NULL, NULL},
> diff --git a/utilities/ovn-trace.c b/utilities/ovn-trace.c
> index 0331eac21..40acf4a64 100644
> --- a/utilities/ovn-trace.c
> +++ b/utilities/ovn-trace.c
> @@ -703,8 +703,7 @@ read_datapaths(void)
>          const struct smap *ids = &sbdb->external_ids;
>
>          dp->sb_uuid = sbdb->header_.uuid;
> -        if (!smap_get_uuid(ids, "logical-switch", &dp->nb_uuid) &&
> -            !smap_get_uuid(ids, "logical-router", &dp->nb_uuid)) {
> +        if (!datapath_get_nb_uuid(sbdb, &dp->nb_uuid)) {
>              dp->nb_uuid = dp->sb_uuid;
>          }
>
> --
> 2.49.0
>
> _______________________________________________
> dev mailing list
> d...@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
Looks good to me, thanks.

Acked-by: Ales Musil <amu...@redhat.com>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to