github.com/tommi2day/gomodules/dblib@v0.0.0-20230217211148-82cdbcf0a79d/tns.go (about)

     1  // Package dblib collection of db func
     2  package dblib
     3  
     4  import (
     5  	log "github.com/sirupsen/logrus"
     6  	"github.com/tommi2day/gomodules/common"
     7  	"os"
     8  	"path/filepath"
     9  	"regexp"
    10  	"strings"
    11  )
    12  
    13  type address struct {
    14  	Host string
    15  	Port string
    16  }
    17  
    18  // TNSEntry structure for holding one entry from tnsnames.ora
    19  type TNSEntry struct {
    20  	Name    string
    21  	Desc    string
    22  	File    string
    23  	Service string
    24  	Servers []address
    25  }
    26  
    27  // TNSEntries Map of tns entries
    28  type TNSEntries map[string]TNSEntry
    29  
    30  func checkSkip(line string) (skip bool) {
    31  	skip = true
    32  	found := false
    33  	reEmpty := regexp.MustCompile(`\S`)
    34  	reComment := regexp.MustCompile(`^#`)
    35  	found = reEmpty.MatchString(line)
    36  	if !found {
    37  		return
    38  	}
    39  	found = reComment.MatchString(line)
    40  	if found {
    41  		return
    42  	}
    43  	skip = false
    44  	return
    45  }
    46  
    47  // GetEntry matches given string to tns entries using with domain part and without
    48  func GetEntry(alias string, entries TNSEntries, domain string) (e TNSEntry, ok bool) {
    49  	match, _ := regexp.MatchString(`^\w+\.`, alias)
    50  	if len(domain) > 0 {
    51  		if match {
    52  			e, ok = entries[strings.ToUpper(alias)] // full qualified
    53  		} else {
    54  			e, ok = entries[strings.ToUpper(alias)+"."+strings.ToUpper(domain)] // short alias+domain
    55  		}
    56  		return
    57  	}
    58  	// no domain, only full match accepted
    59  	e, ok = entries[strings.ToUpper(alias)]
    60  	return
    61  }
    62  
    63  // GetTnsnames map tnsnames.ora entries to an readable structure
    64  func GetTnsnames(filename string, recursiv bool) (TNSEntries, string, error) {
    65  	var tnsEntries = make(TNSEntries)
    66  	var err error
    67  	var domain = ""
    68  	var content []string
    69  	var reIfile = regexp.MustCompile(`(?im)^IFILE\s*=\s*(.*)$`)
    70  	var reNewEntry = regexp.MustCompile(`(?im)^([\w.]+)\s*=(.*)`)
    71  	var tnsAlias = ""
    72  	var desc = ""
    73  
    74  	// try to find sqlnet ora and read domain
    75  	tnsDir := filepath.Dir(filename)
    76  	domain = GetDefaultDomain(tnsDir)
    77  
    78  	// change to current tns file
    79  	wd, _ := os.Getwd()
    80  	log.Debugf("DEBUG: GetTns use %s, wd=%s", filename, wd)
    81  	err = common.ChdirToFile(filename)
    82  	if err != nil {
    83  		log.Errorf("Cannot chdir to %s", filename)
    84  		return tnsEntries, domain, err
    85  	}
    86  
    87  	// use basename from filename to read as i am in this directory
    88  	f := filepath.Base(filename)
    89  	content, _ = common.ReadFileByLine(f)
    90  
    91  	// loop through lines
    92  	for _, line := range content {
    93  		if checkSkip(line) {
    94  			continue
    95  		}
    96  
    97  		// find and load ifiles
    98  		ifile := reIfile.FindStringSubmatch(line)
    99  		if len(ifile) > 0 {
   100  			fn := ifile[1]
   101  			ifileEntries, err := getIfile(fn, recursiv)
   102  			if err == nil {
   103  				for k, v := range ifileEntries {
   104  					tnsEntries[k] = v
   105  				}
   106  			}
   107  			continue
   108  		}
   109  
   110  		// find new entry
   111  		newEntry := reNewEntry.FindStringSubmatch(line)
   112  		i := len(newEntry)
   113  		if i > 0 {
   114  			// save previous entry
   115  			if len(tnsAlias) > 0 && len(desc) > 0 {
   116  				tnsEntries[tnsAlias] = buildEntry(filename, desc, tnsAlias)
   117  			}
   118  			// new entry
   119  			tnsAlias = strings.ToUpper(newEntry[1])
   120  			if i > 2 {
   121  				desc = newEntry[2] + "\n"
   122  			}
   123  		} else {
   124  			desc += line
   125  		}
   126  	}
   127  
   128  	// save last entry
   129  	if len(tnsAlias) > 0 && len(desc) > 0 {
   130  		tnsEntries[tnsAlias] = buildEntry(filename, desc, tnsAlias)
   131  	}
   132  
   133  	// chdir back
   134  	_ = os.Chdir(wd)
   135  	return tnsEntries, domain, err
   136  }
   137  
   138  // read ifile recursive
   139  func getIfile(filename string, recursiv bool) (entries TNSEntries, err error) {
   140  	wd, _ := os.Getwd()
   141  	log.Debugf("read ifile %s, wd=%s", filename, wd)
   142  	entries, _, err = GetTnsnames(filename, recursiv)
   143  	return
   144  }
   145  
   146  // build map for entry
   147  func buildEntry(filename string, desc string, tnsAlias string) TNSEntry {
   148  	var service = ""
   149  	reService := regexp.MustCompile(`(?mi)SERVICE_NAME\s*=\s*([\w.]+)`)
   150  	s := reService.FindStringSubmatch(desc)
   151  	if len(s) > 1 {
   152  		service = s[1]
   153  	}
   154  	entry := TNSEntry{Name: tnsAlias, Desc: desc, File: filename, Service: service, Servers: getServers(desc)}
   155  	log.Debugf("found TNS Alias %s", tnsAlias)
   156  	return entry
   157  }
   158  
   159  // extract address part
   160  func getServers(tnsDesc string) (servers []address) {
   161  	re := regexp.MustCompile(`(?m)HOST\s*=\s*([\w.]+)\s*\)\s*\(\s*PORT\s*=\s*(\d+)`)
   162  	match := re.FindAllStringSubmatch(tnsDesc, -1)
   163  	for _, a := range match {
   164  		if len(a) > 1 {
   165  			host := a[1]
   166  			port := a[2]
   167  			servers = append(servers, address{
   168  				Host: host, Port: port,
   169  			})
   170  			log.Debugf("parsed Host: %s, Port %s", host, port)
   171  		}
   172  	}
   173  	return
   174  }
   175  
   176  // GetDefaultDomain extract names_default_domain from sqlnet.ora
   177  func GetDefaultDomain(path string) (domain string) {
   178  	filename := "sqlnet.ora"
   179  	if path != "" {
   180  		filename = path + "/" + filename
   181  	}
   182  	content, err := common.ReadFileToString(filename)
   183  	if err != nil {
   184  		log.Debugf("Cannot read %s, assume no default domain", filename)
   185  		return ""
   186  	}
   187  	reg := regexp.MustCompile(`(?im)^NAMES.DEFAULT_DOMAIN\s*=\s*([\w.]*)`)
   188  	result := reg.FindStringSubmatch(content)
   189  	if len(result) == 0 {
   190  		log.Debugf("no default domain defined in %s", filename)
   191  		return ""
   192  	}
   193  	domain = result[1]
   194  	log.Infof("default domain: %s", domain)
   195  	return domain
   196  }