Question
Alexey Maslov · Mar 28, 2017

%Installer Manifest: How to ignore several kinds of compilation errors?

Due to a new project I'm getting involved inside %Installer stuff deeper than ever I'd be happy to. Most of it was pretty easy to get, thanks to documentation, articles and SAMPLES example, while I can't dig good solution for one problem. I'm trying to import and compile some classes, willing to ignore two types of errors: 5202 (NothingToCompile) and 5373 (PredecessorClassDoesNotExist - a normal case when SNMP sampling class is compiled for the 1st time, before it was registered). 

I've tried the following: 

<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="5202,5373" Flags="ck" />
</If>

While it was compiled w/o errors,  the mainfest execution failed with error: 

03/28/17-16:20:29:521 (2728) 2 Error: #5002: <PARAMETER>zImport^%Installer.Installer.1

I've found a code inside %Installer.Installer::Import(): 

If $$$ISERR(tSC),(pIgnoreErrors=1)||((","_pIgnoreErrors_",")[(","_$P($system.Status.GetErrorCodes(tSC),",")_",")) {
  Do ..Log(2,"Import","Ignoring Errors: ("_$P($system.Status.GetErrorCodes(tSC),",")_") "_$system.Status.GetErrorText(tSC))
  Set tSC = $$$OK
}

which reassured me that IgnoreErrors can be a comma separated list.

I understand that I can split my classes' export in two files and ignore all errors for 'buggy' one of them by setting IgnoreErrors="1"but still eager to know if more elegant solution exists.

Thank you.

30
1 0 11 309
Log in or sign up to continue

Replies

What does it output when you run it with IgnoreErrors=1 in the Ignoring Errors: log lines? Sometimes it reports multiple errors there (like 5000,5202) -- so by looking at code you'll need to add those in your list: "5000,5202,5000,5373"

Hi Timothy and Sergey,

Timothy, putting the literal codes list inside a variable doesn't help much: I'm getting the same error. 

Thank you anyway.

P.S. The reason of the issue is a wrong code for variable evaluation, e.g. 

<Var Name="AddClassesErrors" Value="5202,5373" />

is transformed to

Do tInstaller.SetVariable("AddClassesErrors","5202","5373")

This just looks like a bug - the generated code is:

Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202","5373","0")

While you would expect:

Do tInstaller.Import(tNSName,tInstaller.Evaluate("${AddonDir}/AddClasses.xml"),"ck","5202,5373","0")

Here's a possible workaround (untested, but the generated code looks better):

<Var Name="AddClassesErrors" Value="5202,5373" />
<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" />
</If>

EDIT: actual workaround (see discussion below) is to use #{<COS_expression>} (see documentation).

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />
<If Condition='#{##class(%File).Exists("${AddonDir}/AddClasses.xml")}'>
  <Import File="${AddonDir}/AddClasses.xml" IgnoreErrors="${AddClassesErrors}" Flags="ck" />
</If>

Can you try IgnoreErrors="""5202,5373"""

That isn't valid XML - I think it'd need to be:

<Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

The &quot; makes it smarter about it being one string, and the extra commas should make it work with the test condition in %Installer.Install:Import:

((","_pIgnoreErrors_",")[(","_$P($system.Status.GetErrorCodes(tSC),",")_","))

This is messy! sad

EDIT: Better option:

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

Timothy,

Both options succeded. Thank you so much! 

Just FYI, your 1st option

<Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

generated a code:

Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""")

and the 2nd one

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

generated:

Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))

 

Right - the first one happens to work in this case, but the second one should work in any case where a comma isn't accepted (and should evaluate to the correct string).

Still looks like a bug -- probably worth reporting somewhere... On the other hand it will probably break current solution once fixed.

Eduard actually reported the same error occuring in a different case a while ago. I added some notes to the bug report earlier (138510).

Timothy,

Both options succeded. Thank you so much! 

Just FYI, your 1st option

<Var Name="AddClassesErrors" Value="&quot;,5202,5373,&quot;" />

generated a code:

Do tInstaller.SetVariable("AddClassesErrors",""",5202,5373,""")

and the 2nd one

<Var Name="AddClassesErrors" Value="#{&quot;5202,5373&quot;}" />

generated:

Do tInstaller.SetVariable("AddClassesErrors",tInstaller.Evaluate("#{""5202,5373""}"))

I've got into another issue trying to write a code which should configure some global mapping to already existing namespace. Here is its skeleton: 

Include %occInclude

/// Create simple global mapping (main DB and cachetemp only)
Class %z.Mapping Extends %Library.RegisteredObject
{

/// Application Definition
XData qMS [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='$L("${NAMESPACE}")=0'>
<!-- Report an error if the namespace wasn't specified -->
<Error Status="$$$NamespaceDoesNotExist">
<Arg Value="${NAMESPACE}"/>
</Error>
</If>

<If Condition='$zcvt("${NAMESPACE}","U")="%SYS"'>
<!-- Report an error if the namespace %SYS -->
<Error Status="$$$GeneralError">
<Arg Value="${NAMESPACE} should not be %SYS"/>
</Error>
</If>

<Invoke Class="%z.Mapping" Method="GetMainDB" CheckStatus="0" Return="MainDB" >
<Arg Value="${NAMESPACE}"/>
</Invoke>

<If Condition='$L("${MainDB}")=0'>
<!-- Report an error if the globals database was not detected -->
<Error Status="$$$GeneralError">
<Arg Value="${NAMESPACE} has no Globals database" />
</Error>
</If>

<Var Name="DBRESOURCE" Value="%DB_%DEFAULT"/>

<Namespace Name="${NAMESPACE}" Create="no" >

<Log Level="0" Text="${NAMESPACE} is ready to be configured using ${MainDB} and ${DBRESOURCE}" />
 
 <Configuration>

<!-- ??? Configure the database that should exist upto this step -->
  <Database Name="${MainDB}"
 Dir="${MainDB}"
 Create="no"
 Resource="${DBRESOURCE}"
 PublicPermissions=""
  />
<!-- ??? Configure the database that should exist upto this step -->

  <GlobalMapping Global="Q(&quot;0!&quot;):(END)"  From="CACHETEMP"/>
  <GlobalMapping Global="Q(&quot;0mseSTATUS&quot;):(&quot;0mseSTATUS~&quot;)"  From="${MainDB}"/>
  <GlobalMapping Global="Qa"     From="CACHETEMP"/>
  <GlobalMapping Global="Qi"     From="CACHETEMP"/>
  <GlobalMapping Global="Qi(&quot;MO&quot;)"     From="${MainDB}"/>
 
  </Configuration>

</Namespace>

</Manifest>
}

/// This is a method generator whose code is generated by XGL.
ClassMethod setup(ByRef pVars, pLogLevel As %Integer, pInstaller As %Installer.Installer, pLogger As %Installer.AbstractLogger) As %Status [ CodeMode = objectgenerator, Internal ]
{
#; Let our XGL document generate code for this method. 
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "qMS")
}

ClassMethod GetMainDB(pNamespace) As %String
{
new $namespace $namespace="%SYS"
sc=##class(Config.Namespaces).Get(pNamespace,.p)
if 'sc $system.OBJ.DisplayError(sc) set ret="" gmdbq
ret=p("Globals")
gmdbq
quit ret
}

/// Invoke the installer passing in some variables
/// zn "USER1" s sc=$system.OBJ.Load("mapping.xml","ck") d:'sc $system.Status.DisplayError(sc)
/// s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc)
/// 
ClassMethod RunInstallWithLog(pLogfile As %String, pLogLevel = 1) As %Status
{
#dim tVars
#dim tStatus As %Status

Set tVars("NAMESPACE") = $zu(5)

// Construct a file logger
#dim tLogger As %Installer.FileLogger = ##class(%Installer.FileLogger).%New(1,pLogfile)

// Invoke the installer
Set tStatus = ..setup(.tVars,pLogLevel,,tLogger)
Do:$$$ISERR(tStatus) $system.OBJ.DisplayError(tStatus)
Quit tStatus
}

}

I've started it at the same Cache instance where I wrote it with following commands:

zn "USER1" s sc=##class(%z.Mapping).RunInstallWithLog($zu(12)_"mapping.log",2) d:'sc $system.Status.DisplayError(sc)

And it works. But if I'm excluding an [unneeded] code fragment that resides between two lines:

<!-- ??? Configure the database that should exist upto this step -->

I'm getting several errors, please see below. Why this fragment is still needed nevertheless I marked my namespace with Create="no" attribute?

​2017-03-31 19:24:51 0 %z.Mapping: Installation starting at 2017-03-31 19:24:51, LogLevel=2
2017-03-31 19:24:51 0 : USER1 is ready to be configured using USER1 and %DB_%DEFAULT
2017-03-31 19:24:51 1 CreateNamespace: Creating namespace USER1 using /
2017-03-31 19:24:51 2 CreateNamespace: Modifying namespace USER1
2017-03-31 19:24:51 0 %z.Mapping: ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","")
ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
2017-03-31 19:24:51 0 %z.Mapping: ERROR #ConfigFailed: Unknown status code: <Ins>ConfigFailed (,,,,,,,)
  > ERROR #5002: Cache error: <SUBSCRIPT>%LoadData+6^Config.Databases.1 ^SYS("CONFIG","CACHE","Databases","")
  > ERROR #5659: Property 'Config.Namespaces::Globals(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
  > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
  > ERROR #5659: Property 'Config.Namespaces::Routines(3@Config.Namespaces,ID=CACHE||Namespaces||USER1)' required
  > ERROR #7202: Datatype value '' length less than MINLEN allowed of 1
2017-03-31 19:24:51 0 %z.Mapping: Installation failed at 2017-03-31 19:24:51
2017-03-31 19:24:51 0 %Installer: Elapsed time .178622s