Updating AAI wit resource info : part3 35/102235/2
authorvamshi.nemalikonda <vn00480215@techmahindra.com>
Mon, 24 Feb 2020 13:21:37 +0000 (13:21 +0000)
committervamshi.nemalikonda <vn00480215@techmahindra.com>
Wed, 4 Mar 2020 15:46:27 +0000 (15:46 +0000)
adding api module. Issue-ID: MULTICLOUD-457

Change-Id: Icf97d13a6c683b8f09491491ed2d2709f4700107
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
Updating AAI wit resource info : part3

review comments fixes. Issue-ID: MULTICLOUD-457

Change-Id: Icf97d13a6c683b8f09491491ed2d2709f4700107
Signed-off-by: vamshi.nemalikonda <vn00480215@techmahindra.com>
src/inventory/api/aaipullapi.go [new file with mode: 0644]
src/inventory/api/aaipullapi_test.go [new file with mode: 0644]
src/inventory/api/aaipushapi.go [new file with mode: 0644]
src/inventory/api/aaipushapi_test.go [new file with mode: 0644]
src/inventory/api/k8spluginapi.go [new file with mode: 0644]
src/inventory/api/k8spluginapi_test.go [new file with mode: 0644]

diff --git a/src/inventory/api/aaipullapi.go b/src/inventory/api/aaipullapi.go
new file mode 100644 (file)
index 0000000..c8e4ca3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "crypto/tls"
+       "encoding/json"
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       log "github.com/onap/multicloud-k8s/src/inventory/logutils"
+       util "github.com/onap/multicloud-k8s/src/inventory/utils"
+       "io/ioutil"
+       "net/http"
+       "os"
+)
+
+func GetTenant(cloudOwner, cloudRegion string) string {
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       apiToCR := AAI_URI + ":" + AAI_Port + con.AAI_EP + con.AAI_CREP + "cloud-region/" + cloudOwner + "/" + cloudRegion + "?depth=all"
+       req, err := http.NewRequest(http.MethodGet, apiToCR, nil)
+       if err != nil {
+               log.Error("Error while constructing request for Tenant API")
+               return
+
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               log.Error("Error while executing request for Tenant API")
+               return
+       }
+
+       defer res.Body.Close()
+
+       body, err := ioutil.ReadAll(res.Body)
+       if err != nil {
+
+               log.Error("Can't read Tenant response")
+               return
+
+       }
+
+       var tenant con.Tenant
+
+       json.Unmarshal([]byte(body), &tenant)
+
+       for k, v := range tenant.Tenants {
+               if k == "tenant" {
+                       for _, val := range v {
+                               return val.TenantId
+
+                       }
+               }
+       }
+
+       return ""
+
+}
diff --git a/src/inventory/api/aaipullapi_test.go b/src/inventory/api/aaipullapi_test.go
new file mode 100644 (file)
index 0000000..4d838e3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "crypto/tls"
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       util "github.com/onap/multicloud-k8s/src/inventory/utils"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "strings"
+       "testing"
+)
+
+func TestGetTenant(t *testing.T) {
+
+       cloudOwner := "CloudOwner"
+       cloudRegion := "RegionOne"
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       uri := AAI_URI + ":" + AAI_Port + con.AAI_EP + con.AAI_CREP + "cloud-region/" + cloudOwner + "/" + cloudRegion + "?depth=all"
+
+       req, err := http.NewRequest("GET", uri, nil)
+
+       util.SetRequestHeaders(req)
+
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if res.StatusCode != http.StatusOK {
+
+               t.Fail()
+       }
+
+       if p, err := ioutil.ReadAll(res.Body); err != nil {
+               t.Fail()
+       } else {
+               if strings.Contains(string(p), "Error") {
+                       t.Errorf("header response shouldn't return error: %s", p)
+               } else if !strings.Contains(string(p), `tenant`) {
+                       t.Errorf("header response doen't match:\n%s", p)
+               }
+       }
+}
diff --git a/src/inventory/api/aaipushapi.go b/src/inventory/api/aaipushapi.go
new file mode 100644 (file)
index 0000000..7faf498
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "bytes"
+       "crypto/tls"
+       "encoding/json"
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       log "github.com/onap/multicloud-k8s/src/inventory/logutils"
+       util "github.com/onap/multicloud-k8s/src/inventory/utils"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "strconv"
+)
+
+/* Pushes each pod related details as vservers inot A&AI
+
+{
+                "vserver-id": "example20",
+                "vserver-name": "POD-NAME",
+                "vserver-name2": "Relese-name/Profile-name of the POD (Labels:release=profile-k8s)",
+                "prov-status": "NAMESPACEofthPOD",
+                "vserver-selflink": "example-vserver-selflink-val-57201",
+                "in-maint": true,
+                "is-closed-loop-disabled": true,
+                "l-interfaces": {
+                                "l-interface": [{
+                                                "interface-name": "example-interface-name-val-20080",
+                                                                                               "is-port-mirrored": true,
+                                                                                               "in-maint": true,
+                                                                                               "is-ip-unnumbered": true,
+                                                "l3-interface-ipv4-address-list": [{
+                                                                "l3-interface-ipv4-address": "IP_Address",
+                                                                "l3-interface-ipv4-prefix-length": "PORT"
+                                                }]
+                                }]
+                }
+}
+
+*/
+func PushVservers(podInfo con.PodInfoToAAI, cloudOwner, cloudRegion, tenantId string) string {
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       payload := "{\"vserver-name\":" + "\"" + podInfo.VserverName + "\"" + ", \"vserver-name2\":" + "\"" + podInfo.VserverName2 + "\"" + ", \"prov-status\":" + "\"" + podInfo.ProvStatus + "\"" + ",\"vserver-selflink\":" + "\"example-vserver-selflink-val-57201\", \"l-interfaces\": {\"l-interface\": [{\"interface-name\": \"example-interface-name-val-20080\",\"is-port-mirrored\": true,\"in-maint\": true,\"is-ip-unnumbered\": true,\"l3-interface-ipv4-address-list\": [{\"l3-interface-ipv4-address\":" + "\"" + podInfo.I3InterfaceIPv4Address + "\"" + ",\"l3-interface-ipv4-prefix-length\":" + "\"" + strconv.FormatInt(int64(podInfo.I3InterfaceIPvPrefixLength), 10) + "\"" + "}]}]}}"
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       url := AAI_URI + ":" + AAI_Port + con.AAI_EP + "cloud-region/" + cloudOwner + "/" + cloudRegion + "/tenants/tenant/" + tenantId + "/vservers/vserver/" + podInfo.VserverName
+
+       var jsonStr = []byte(payload)
+
+       req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonStr))
+
+       if err != nil {
+               log.Error("Error while constructing Vserver PUT request")
+               return
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := &http.Client{}
+       resp, err := client.Do(req)
+       if err != nil {
+               log.Error("Error while executing Vserver PUT api")
+               return
+       }
+       defer resp.Body.Close()
+
+       return podInfo.VserverName
+}
+
+/* This links vservers to vf-module request payload */
+func LinkVserverVFM(vnfID, vfmID, cloudOwner, cloudRegion, tenantId string, relList []con.RelationList) {
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       apiToCR := AAI_URI + ":" + AAI_Port + con.AAI_EP + con.AAI_NEP + "/" + vnfID + "/vf-modules"
+       req, err := http.NewRequest(http.MethodGet, apiToCR, nil)
+       if err != nil {
+               log.Error("Error while constructing VFModules GET api request")
+               return
+
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               log.Error("Error while executing VFModules GET api")
+               return
+       }
+
+       defer res.Body.Close()
+
+       body, err := ioutil.ReadAll(res.Body)
+       if err != nil {
+
+               log.Error("Error while reading vfmodules API response")
+               return
+
+       }
+
+       var vfmodules con.VFModules
+
+       json.Unmarshal([]byte(body), &vfmodules)
+
+       vfmList := vfmodules.VFModules
+
+       for key, vfmodule := range vfmList {
+
+               if vfmodule.VFModuleId == vfmID {
+
+                       vfmodule.RelationshipList = map[string][]con.RelationList{"relationship": relList}
+
+                       vfmList = append(vfmList, vfmodule)
+
+                       vfmList[key] = vfmList[len(vfmList)-1] // Copy last element to index i.
+                       vfmList = vfmList[:len(vfmList)-1]
+
+                       //update vfmodule with vserver data
+
+                       vfmPayload, err := json.Marshal(vfmodule)
+
+                       if err != nil {
+
+                               log.Error("Error while marshalling vfmodule linked vserver info response")
+                               return
+
+                       }
+
+                       pushVFModuleToAAI(string(vfmPayload), vfmID, vnfID)
+               }
+
+       }
+
+}
+
+/*  Pushes vf-module enriched with vserver information */
+func pushVFModuleToAAI(vfmPayload, vfmID, vnfID string) {
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+       url := AAI_URI + ":" + AAI_Port + con.AAI_NEP + vnfID + "/vfmodules/vf-module/" + vfmID
+
+       var jsonStr = []byte(vfmPayload)
+
+       req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonStr))
+
+       if err != nil {
+               log.Error("Error while constructing a VFModule request to AAI")
+               return
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := &http.Client{}
+       resp, err := client.Do(req)
+       if err != nil {
+
+               log.Error("Error while executing PUT request of VFModule to AAI")
+               return
+
+       }
+
+       defer resp.Body.Close()
+
+}
diff --git a/src/inventory/api/aaipushapi_test.go b/src/inventory/api/aaipushapi_test.go
new file mode 100644 (file)
index 0000000..6c0dea3
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "bytes"
+       "crypto/tls"
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       util "github.com/onap/multicloud-k8s/src/inventory/utils"
+       "net/http"
+       "os"
+       "testing"
+)
+
+func TestPushVservers(t *testing.T) {
+
+       cloudOwner := "CloudOwner"
+       cloudRegion := "RegionOne"
+       tenantId := "tenant123"
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       payload := "{\"vserver-name\":" + "\"" + "pod123" + "\"" + ", \"vserver-name2\":" + "\"" + "profile123" + "\"" + ", \"prov-status\":" + "\"" + "default" + "\"" + ",\"vserver-selflink\":" + "\"example-vserver-selflink-val-57201\", \"l-interfaces\": {\"l-interface\": [{\"interface-name\": \"example-interface-name-val-20080\",\"is-port-mirrored\": true,\"in-maint\": true,\"is-ip-unnumbered\": true,\"l3-interface-ipv4-address-list\": [{\"l3-interface-ipv4-address\":" + "\"" + "10.214.22.220" + "\"" + ",\"l3-interface-ipv4-prefix-length\":" + "\"" + "667" + "\"" + "}]}]}}"
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       url := AAI_URI + ":" + AAI_Port + con.AAI_EP + "cloud-region/" + cloudOwner + "/" + cloudRegion + "/tenants/tenant/" + tenantId + "/vservers/vserver/" + "pod123"
+
+       var jsonStr = []byte(payload)
+
+       req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonStr))
+
+       if err != nil {
+
+               t.Error("Failed: Error consructing request")
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := &http.Client{}
+       resp, err := client.Do(req)
+       if err != nil {
+
+               t.Error("Failed: Error while executing request ")
+       }
+
+       if resp.StatusCode != http.StatusOK {
+               t.Fatalf("recieved unexpeted response ")
+       }
+
+}
+
+func TestLinkVserverVFM(t *testing.T) {
+
+       vnfID := "vnf123456"
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       apiToCR := AAI_URI + ":" + AAI_Port + con.AAI_EP + con.AAI_NEP + "/" + vnfID + "/vf-modules"
+       req, err := http.NewRequest(http.MethodGet, apiToCR, nil)
+       if err != nil {
+
+               t.Error("Failed: Error while constructing new request")
+
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+
+               t.Error("Failed: Error while executing request")
+       }
+
+       if res.StatusCode != http.StatusOK {
+               t.Fatalf("recieved unexpeted response ")
+       }
+
+}
+
+func TestPushVFModuleToAAI(t *testing.T) {
+
+       vnfID := "vnf123456"
+       vfmID := "vfm123456"
+       vfmPayload := ""
+
+       AAI_URI := os.Getenv("onap-aai")
+       AAI_Port := os.Getenv("aai-port")
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+       url := AAI_URI + ":" + AAI_Port + con.AAI_NEP + vnfID + "/vfmodules/vf-module/" + vfmID
+
+       var jsonStr = []byte(vfmPayload)
+
+       req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonStr))
+
+       if err != nil {
+               t.Error("Failed: Error while executing request")
+
+       }
+
+       util.SetRequestHeaders(req)
+
+       client := &http.Client{}
+       resp, err := client.Do(req)
+       if err != nil {
+
+               t.Error("Failed: Error while executing request")
+       }
+
+       if resp.StatusCode != http.StatusOK {
+               t.Fatalf("recieved unexpeted response ")
+       }
+
+}
diff --git a/src/inventory/api/k8spluginapi.go b/src/inventory/api/k8spluginapi.go
new file mode 100644 (file)
index 0000000..4348d28
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       log "github.com/onap/multicloud-k8s/src/inventory/logutils"
+       utils "github.com/onap/multicloud-k8s/src/inventory/utils"
+       k8sint "github.com/onap/multicloud-k8s/src/k8splugin/internal/app"
+       k8scon "github.com/onap/multicloud-k8s/src/k8splugin/internal/connection"
+
+       "encoding/json"
+       "net/http"
+       "os"
+)
+
+func ListInstances() []string {
+
+       MK8S_URI := os.Getenv("onap-multicloud-k8s")
+       MK8S_Port := os.Getenv("multicloud-k8s-port")
+
+       instancelist := MK8S_URI + ":" + MK8S_Port + con.MK8S_EP
+       req, err := http.NewRequest(http.MethodGet, instancelist, nil)
+       if err != nil {
+
+               log.Error("Something went wrong while listing resources - contructing request")
+               return
+       }
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               log.Error("Something went wrong while listing resources - executing request")
+               return
+       }
+
+       defer res.Body.Close()
+
+       decoder := json.NewDecoder(res.Body)
+       var rlist []k8sint.InstanceMiniResponse
+       err = decoder.Decode(&rlist)
+
+       resourceList := utils.ParseListInstanceResponse(rlist)
+
+       return resourceList
+
+}
+
+func GetConnection(cregion string) k8scon.Connection {
+
+       MK8S_URI := os.Getenv("onap-multicloud-k8s")
+       MK8S_Port := os.Getenv("multicloud-k8s-port")
+
+       instancelist := MK8S_URI + ":" + MK8S_Port + con.MK8S_CEP + cregion
+       req, err := http.NewRequest(http.MethodGet, instancelist, nil)
+       if err != nil {
+
+               log.Error("Something went wrong while getting Connection resource - contructing request")
+               return
+       }
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               log.Error("Something went wrong while getting Connection resource - executing request")
+               return
+       }
+
+       defer res.Body.Close()
+
+       decoder := json.NewDecoder(res.Body)
+       var connection k8scon.Connection
+       err = decoder.Decode(&connection)
+
+       return connection
+
+}
+
+func CheckStatusForEachInstance(instanceID string) k8sint.InstanceStatus {
+
+       MK8S_URI := os.Getenv("onap-multicloud-k8s")
+       MK8S_Port := os.Getenv("multicloud-k8s-port")
+
+       instancelist := MK8S_URI + ":" + MK8S_Port + con.MK8S_EP + instanceID + "/status"
+
+       req, err := http.NewRequest(http.MethodGet, instancelist, nil)
+       if err != nil {
+               log.Error("Error while checking instance status - building http request")
+               return
+       }
+
+       client := http.DefaultClient
+       resp, err := client.Do(req)
+       if err != nil {
+
+               log.Error("Error while checking instance status - making rest request")
+               return
+       }
+
+       defer resp.Body.Close()
+
+       decoder := json.NewDecoder(resp.Body)
+       var instStatus k8sint.InstanceStatus
+       err = decoder.Decode(&instStatus)
+
+       return instStatus
+}
diff --git a/src/inventory/api/k8spluginapi_test.go b/src/inventory/api/k8spluginapi_test.go
new file mode 100644 (file)
index 0000000..6eb1648
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+Copyright 2020  Tech Mahindra.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+       "crypto/tls"
+       con "github.com/onap/multicloud-k8s/src/inventory/constants"
+       util "github.com/onap/multicloud-k8s/src/inventory/utils"
+       "io/ioutil"
+       "net/http"
+       "os"
+       "strings"
+       "testing"
+)
+
+func TestListInstances(t *testing.T) {
+
+       http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
+
+       MK8S_URI := os.Getenv("onap-multicloud-k8s")
+       MK8S_Port := os.Getenv("multicloud-k8s-port")
+
+       instancelist := MK8S_URI + ":" + MK8S_Port + con.MK8S_EP
+       req, err := http.NewRequest(http.MethodGet, instancelist, nil)
+
+       util.SetRequestHeaders(req)
+
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       client := http.DefaultClient
+       res, err := client.Do(req)
+
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       if res.StatusCode != http.StatusOK {
+
+               t.Fail()
+       }
+
+       if p, err := ioutil.ReadAll(res.Body); err != nil {
+               t.Fail()
+       } else {
+               if strings.Contains(string(p), "Error") {
+                       t.Errorf("header response shouldn't return error: %s", p)
+               } else if !strings.Contains(string(p), `cloud-region`) {
+                       t.Errorf("header response doen't match:\n%s", p)
+               }
+       }
+}