Quantcast
Viewing all 141 articles
Browse latest View live

HANA OOM Error Tip #1 - Partition Tables Correctly

If your HANA system is regularly experiencing OOM (Out Of Memory) errors, then there are a number of things that you can do to try and reduce memory consumption.

Tip #1:  Partition Large Tables Correctly
If there are large Column Store tables in your HANA system, you should partition them.
Whilst this is an absolute must in a HANA scale-out scenario (for enabling multi-node parallelism), it might not be so obvious that it can also help in a single node HANA system.
Partitioning a column table means that only the required partitions of the table are loaded into memory when accessed, you would think.

Partitioning a large table into smaller chunks will therefore help to reduce the memory usage of the table during SQL queries and also during updates.
During updates, each partition gets its own delta cache area.

Choosing how to partition a table is slightly more difficult and will depend on whether the table is a custom table, SAP standard table or other.
Plus, you will need to know what and how queries or updates are executed against the table.  A period of monitoring is suggested, to enable to you collect the required information to be able to make a decision.

One thing you should try to do, is partition the table using the most logical, selective columns.
Read on for a simple example with a twist!

A simple example, a single node HANA system has 1 large column table T1.
The table is partitioned into ranges based on the date column INVOICEDATE:

CREATE COLUMN TABLE "DARRYL"."T1" ("INVOICEREF" VARCHAR(1) NOT NULL ,
     "INVOICEDATE" DAYDATE CS_DAYDATE NOT NULL ) UNLOAD PRIORITY 5 AUTO MERGE WITH PARAMETERS ('PARTITION_SPEC' = 'RANGE year(INVOICEDATE) 2000-2001,2001-2002,2002-2003,*')
;
CREATE UNIQUE INDEX "I1" ON "DARRYL"."T1" ( "INVOICEREF" ASC ) NONLEAF PARTIAL KEY LENGTH 1;

As you can see, I've created 3 partitions by year:  2000 to 2001,  2001 to 2002 and 2002 to 2003.
This will actually create 4 partitions:  year 2000,  year 2001, year 2002 and year <OTHER>.

Image may be NSFW.
Clik here to view.
HANA Table Distribution


Insert 5 records into the table:

insert into darryl.t1 (INVOICEREF,INVOICEDATE) values('1','2000-01-01')
insert into darryl.t1 (INVOICEREF,INVOICEDATE) values('2','2001-01-01')
insert into darryl.t1 (INVOICEREF,INVOICEDATE) values('3','2002-01-01')
insert into darryl.t1 (INVOICEREF,INVOICEDATE) values('4','2003-01-01')
insert into darryl.t1 (INVOICEREF,INVOICEDATE) values('5','2004-01-01')

Inside the Table Distribution tab, you will now see that the records have been inserted according to their values into the respective partitions (see Raw Record Count field on the right):

Image may be NSFW.
Clik here to view.
HANA Table Distribution


The last two records for year 2004 and 2003 are in the fourth partition.
You can also see that each partition has a Delta Size, and that the Delta Size for the fourth partition with the most records, is larger than the other partitions.
Unload the table from memory:

Image may be NSFW.
Clik here to view.
HANA Unload Table from memory


Refreshing the Table Distribution tab now shows the table partitions to have zero size in memory:

Image may be NSFW.
Clik here to view.
HANA Table Distribution


Now select the records for the years 2004 and 2005 only:

select * from darryl.t1 where invoicedate in ('2004-01-01','2003-01-01')

Refreshing the Table Distribution tab now shows the tables to have non-zero size in memory for ALL partitions!

Image may be NSFW.
Clik here to view.
HANA Table Distribution


All of the records from all of the partitions appear to be loaded!
What went wrong?
Well, it's simple, we didn't create an index on the column INVOICEDATE.
This forced HANA to scan through the entire table to access the required records, meaning that it needed to load them all into memory.

Let's create an index in INVOICEDATE:

CREATE UNIQUE INDEX "I1" ON "DARRYL"."T1" ( "INVOICEREF" ASC ) NONLEAF PARTIAL KEY LENGTH 1;

Unload the table from memory:

Image may be NSFW.
Clik here to view.
HANA Unload Table from memory


Refreshing the Table Distribution tab now shows the tables to have zero size in memory:

Image may be NSFW.
Clik here to view.
HANA Table Distribution


Now re-execute the SELECT statement:

select * from darryl.t1 where invoicedate in ('2004-01-01','2003-01-01')

Once again, on the Table Distribution tab, we can see that it has accessed all partitions, AGAIN!:

Image may be NSFW.
Clik here to view.
HANA Table Distribution


What went wrong this time?  Well, HANA doesn't yet have any statistics on the table data, so it simply ignored the index.
If you now unload the table from memory once again (we haven't done anything else):

Image may be NSFW.
Clik here to view.
HANA Unload Table from memory


Now re-execute the SELECT statement:

select * from darryl.t1 where invoicedate in ('2004-01-01','2003-01-01')

Look at the Table Distribution tab:

Image may be NSFW.
Clik here to view.
HANA Table Distribution


You can see that HANA has now only accessed the final partition of the table.  The other partitions have not been loaded into memory.
At first I thought this feature might be due to statistics, so I tried removing them from the table T1 (drop statistics on T1;).  Then I retried the process of unloading and re-running the query.  This had no effect, HANA correctly went straight to the fourth partition.
This left me with one other option, the Plan Cache.

Clearing the Plan Cache using:

ALTER SYSTEM CLEAR SQL PLAN CACHE

I then re-ran the test by unloading the table from memory:

Image may be NSFW.
Clik here to view.
HANA Unload Table from memory


Re-executing the SQL SELECT:

select * from darryl.t1 where invoicedate in ('2004-01-01','2003-01-01')

Look at the Table Distribution tab:

Image may be NSFW.
Clik here to view.
HANA Table Distribution


Bingo!
The Plan Cache was storing some form of execution plan statistics that meant that it was accessing the fourth partition straight away.
Each time the table is unloaded, the statistics from the existing Plan Cache remain and are re-used upon next execution of the query, which means HANA is able to go straight to the fourth partition.

Summary:
Partitioning is a great way of parallelising access to a table in HANA.
It also serves to help reduce memory by only loading specific partitions into memory when they are required.
In order to effectively use partitioning, you need to partition on an indexed column.
The initial access of a newly partition table with a new index, does not enable the benefits of partition until the second subsequent access of the table due to the Plan Cache.  A method/process of pre-loading the execution plan/statistics into the cache is required.

SAP HANA Backup Allocation Failed

During a HANA backup, you get an "Allocation Failed" error.
This is caused by a lack of memory.  If possible, increase the memory available to HANA by increasing the free memory at the O/S level (e.g. shutting down other HANA instances), or increase the global allocation limit.

Encrypting HANA Data Volumes

Out of the box, the HANA database is not encrypted.
For those businesses that demand encryption, from within HANA Studio you can activate the encryption of the HANA data volumes.
The HANA documentation supplied with SPS7, suggests that the recommended approach is to encrypt the data volumes as part of the HANA installation process.  This is due to the "Copy-on-write" method used to persist data on disk.  An already used database may still have unencrypted data pages in it, even after encryption is enabled.

You should note that the words "data volumes" means the location of the database files for the indexserver and the statistics server, which is usually something like "/hana/data/<SID>/mnt0000<n>/hdb0000<n>/*.dat".

Tip: You can check which values for "<n>" in the above, will be used, by checking the "Landscape Overview" tab and the "Volumes" tab within that.

Prerequisites:
- SAP recommend changing the SSFS encryption key after installation of the HANA system.  This key is used to store the data volume encryption key and others.
- Disk space of the data volume areas (we discuss this below anyway).
- Take a cold backup of the HANA DB area and config files.
- Access to the HANA Studio (you can do this with SQL access and hdbsql, but I don't show this).

Let's begin:

From within HANA Studio, open the "Security" tab and navigate to the "Data Volume Encryption" tab.
You will notice that encryption is not already enabled.
Tick the "Activate encryption of data volumes" tick box, then click the "Execute" button:

Image may be NSFW.
Clik here to view.
HANA volume encryption activation


Image may be NSFW.
Clik here to view.
HANA data volume encryption execute


The encryption process will start immediately ("Encryption Pending" then "Encryption Running"):

Image may be NSFW.
Clik here to view.
HANA volume encryption pending


The status is updated when each server process finishes:

Image may be NSFW.
Clik here to view.
HANA volume encryption running


Once the encryption process is completed successfully, the tick box is enabled again for you to be able to de-encrypt the volumes should you wish to reverse the process:

Image may be NSFW.
Clik here to view.
HANA volume encryption encrypted


My small (empty) HANA DB @ sps7, took approximately 10 minutes to encrypt.
The data volume sizes increased as part of the encryption process.
The indexserver data volume went from 324MB used to 341MB used:

Image may be NSFW.
Clik here to view.
HANA volume encryption indexserver size


Image may be NSFW.
Clik here to view.
HANA volume encryption indexserver size


Of more importance is the dramatic increase in the allocated (Total Size).
It's gone from 513MB to 896MB!  You will need to be aware of the disk space you may need before enabling encryption.

The statistics server is smaller and went from 80MB used to 101MB used:

Image may be NSFW.
Clik here to view.
Snap668 2014-06-26, 11_26_17


Image may be NSFW.
Clik here to view.
image


Again, notice that we've got an increased allocation from 320MB to 400MB.

Validate the encryption status using an SQL console:

SELECT * FROM M_PERSISTENCE_ENCRYPTION_STATUS;

Image may be NSFW.
Clik here to view.
image



What are the implications for encryption?

Well, the data volumes have been encrypted, but we've not seen anything about encrypting the logs or the backups.
Also, to preserve the performance of the HANA system in memory, the data is decrypted when read from the disk into memory.

Is encrypting the HANA DB transaction logs feasible?  Probably not.  There are various whitepapers detailing the issues of encryption of data stored on SSDs.  Since the majority of high performance appliance resellers use SSDs for the HANA DB transaction logs, the use of software layer encryption on SSDs is not worth the effort and would probably reduce the performance of the database.  Instead, using the SSD hardware layer encryption may prove useable, but this is only worth while if you think that the SSD could be physically stolen.  SSD hardware encryption doesn't prevent an intruder at the O/S level from seeing the data.

Is encrypting the HANA DB backups (data and logs) feasible?  Yes this is definitely something that should be employed.  There are 3rd party backup tools that will take a copy of the HANA backup files, then encrypt and store elsewhere.  But these tools may not support secure wipe, so the disk location of the backup files would potentially still contain the data.  Using backup tools certified for SAP with the BackInt interface would be better, since the data doesn't touch the disks, it's piped straight to the backup device via the "network".

There is possibly some slight performance impact from encrypting the data volumes.  The data files are written to at least every 5 minutes.  They are also read from during HANA start up.  These I/O operations will be the same speed as they were before, except there will be some additional software routines involved to perform the encryption, which will use slightly more memory and mean a slight delay between the data being read/written to/from the disk.

Be aware of bugs in the backup/restore process.
I've seen mention of bugs in other software products where an encrypted backup is void due to a software bug.  Ensure that you test your solution thoroughly.

SAP recommend that you change the Page Encryption Key (used to encrypt the data volumes) regularly as per your organisations standards.  This may yet again increase the allocated size.

Summary:
- Data volume encryption is easy and fast in HANA.  There's not really any reason to not implement it.  Beware of implementing in an already populated database and ensure you change the keys regularly.
- Backups and transaction log volumes are not encrypted and for the logs there's a good reason why you may not want to.
- Performance in certain scenarios could be affected slightly.
- You should attempt to implement a supported Backint capable backup product with encryption, since the backups with this method don't touch the unencrypted disks.
- Encryption can be performed and validated at the SQL command level.
- Be aware that data volume encryption in HANA will require more disk space to be allocated to the data volumes, increasing the footprint of the HANA database by as much as 50%.

HANA DB Re-initialisation Without Reinstall

Scenario:  You have a small test system or PoC system and you want to revert or recreate the HANA DB like you've just opened the box and installed it from new, but without the hassle of the reinstall.

This is completely possible using the hdbnsutil command line program.
With HANA shutdown, connect to the HANA server via SSH as the <sid>adm Linux user, then run the hdbnsutil command line program as follows:

hana01:/usr/sap/H10/HDB10/exe> hdbnsutil -initTopology

checking for inactive nameserver ...
nameserver hana01:31001 not responding.
creating persistence ...
run as transaction master writing initial topology...
writing initial license: status check = 2
done

As you will see, it recreates the persistence layer (database) and also re-creates the license.
You will need to reinstall your HANA system license after the re-initialisation process is complete.
On a slow system, the process took approximately 2 minutes.

Due to the size of my system, I am unable to tell you if this process destroys any specific configuration.  From what I can tell, the existing global.ini, nameserver.ini and indexserver.ini are kept.

You should also note that the SYSTEM user password is reset to its default value of "master".
Plus, if you have enabled encryption, the reinitialised data volumes will be re-encrypted unless you de-check the checkbox on the Data Volume Encryption tab inside the Security tab, prior to reinitialisation.

Blog Readership, Almost 7,000

Since December 2013, my blog readership has really taken off.
It's nearly at 7,000 page views per month:

Image may be NSFW.
Clik here to view.
image


Which is the most popular post of all time, well it's still my multi-post guide to basic performance tuning a SAP system (published in 2011), but very closely followed by my recent HANA installation into a VM post.
You will also notice that a lot of readers are looking for the FICO authorisation objects F_REGU_BUK and F_REGU_KOA:

Image may be NSFW.
Clik here to view.
image


We can see that the US is very dominant in the stats, secondly India and then Germany.  The UK is in 4th place:

Image may be NSFW.
Clik here to view.
image


It's a close call between IE and Chrome in the browsers used.
Good job I've written my free extension for searching SAP notes, in Chrome!

Image may be NSFW.
Clik here to view.
image


Thanks for reading everyone.
I hope I've provided you with the solutions you've sought.

Darryl

Power Notes Searcher Updated to v1.1

The Chromium project has recently (May 2014) fixed a bug in the Chrome web browser which means that users of my Power Notes Searcher Google Chrome extension may have seen an issue with the table ordering in the SAP Notes history table.

I have now made a slight correction to the extension and v1.1 is now available for update/install in the Google Chrome Extensions Web Store (or from the link on my initial blog post here).

If you haven't already installed my extension, give it a go.  You don't know what you're missing!

Netgear ReadyNAS & DynDNS Replacement

With the demise of free DNS services from companies such as DynDNS, I was left with no free method of reliably accessing my Netgear ReadyNAS via the internet.

Before these free services closed down, my ReadyNAS was configured to automatically update it's IP address to the DynDNS service website, which allowed me to use a DynDNS provided host address (e.g. mydevice.dyndns.org) to access the ReadyNAS no matter what it's IP address was changed to by my home ISP provider.

When the free service eventually stopped, I decided that I didn't want to pay for such a small service offering, so I wrote a small script to perform the same sort of service.
The script simply checks my current ISP assigned IP address and compares it to the previous run of the script.  If the IP address has changed between checked, the script sends an email to my email account, which I can then use to access the device by directly entering the IP address into my web browser.

It's not neat, but it works.
Here's how I did it.

What you'll need:
- Root access to your ReadyNAS (you need the SSH addon installed).
- Configure email alerting on your ReadyNAS (do this in FrontView by giving it access to your email provider e.g. smtp.gmail.com).

Step 1 - Create the script.

As the root user connected to your ReadyNAS via SSH, use vi to create a small script in the home directory of the root user:

# cd $HOME
# vi check_ip_address

#!/bin/bash
#--------------------------------------------------------
#  Get IP address and mail it to us.
#  Place this script in $HOME/root
#  Call it from crontab.
#
#-------------------------------------------------------- user="ReadyNAS"
email_from=$(grep '^from' /etc/msmtprc | cut -f2 -d'')
email_addr=`grep email /etc/frontview/alert.conf | awk -F '!!''{ print $2 }' | awk -F ',''{ print $1 "" $2 "" $3 }'`

subject="IP Address Changed" newfile=/tmp/myip.html
oldfile=/tmp/myip.old

# Call a generic site to get our IP.
wget --no-cookies --no-cache --user-agent="Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36" --no-verbose --output-document=/tmp/myip.html http://www.askapache.com/online-tools/whoami/

# Read output.
myipnew=`awk -F "tt>"'{ if($2 ~/\./){ sub(/<\//,"",$2); print $2} }' $newfile`
myipold=`cat $oldfile 2>&1`
if [ "$myipnew" != "$myipold" ]; then
  # IP is different to last, so save it and mail it.
  echo "$myipnew"> $oldfile
  mesg="New IP Address: $myipnew"   headers="From: $email_from\nTo: $email_addr\n"
  full_content="${headers}Subject: ${subject}\n${content}\n\n$mesg"
  echo -e "$full_content" | /usr/sbin/sendmail -B8BITMIME -f $email_from $email_addr &> /dev/null
  echo "RC: $?"
fi

Set permissions on the script:

# chmod 750 check_ip_address

Step 2 - Schedule the Script.

Add the script to a cron schedule:

# export EDITOR=vi
# crontab -e
30 09 * * * /root/check_ip_address
30 14 * * * /root/check_ip_address

The above schedules the script to run at 09:30am every day, and again at 14:30.
That's it!

SAP NW 7.31 Java - HTTP Tracing

Scenario:  You would like to trace a HTTP connection to a Netweaver 7.31 Java stack.

Prior to NW 7.31 you would have enabled tracing in either the old Java Administrative Tool, or newer Netweaver Administrator by setting the "enable" flag for tracing parameters of the HTTPProvider dispatcher service (see my previous blog article here).

Since Netweaver 7.31, the Java stack now includes an ICM (Internet Communication Manager) which is the main access point to which HTTP communications are routed to the Java server nodes.
Therefore, tracing in NW 7.31 Java is actually more like the ABAP stack tracing, because you simply increase the trace level in the ICM and then check the dev_icm trace file.

So the next question is, how do you access the ICM to increase the trace level in a Java stack?
Well this can be performed with the help of the SAPMC (only one "M" and not to be confused with SAP MMC for Windows).
The SAPMC was introduced in later NW 7.0 patches and is part of the sapstartsrv service.
The sapstartsrv actually listens on TCP port 5<##>13.  So if you have access through your firewall, navigate to http://<app server>:5<##>13   and the the Java based applet will start up (note that you'll need Java 1.4 or above on your PC).

Once loaded, you can see the SAP MC and expand the "AS Java" branch to locate the "Process Table" item:

Image may be NSFW.
Clik here to view.
SAP MC management console


From the "Process Table" you should be able to right click the ICM (on the right) and increment the trace level:

Image may be NSFW.
Clik here to view.
SAP MC increase ICM trace level


The trace level is dynamically increased.
To see the trace file, right click again and select "Show Developer Trace":

Image may be NSFW.
Clik here to view.
SAP MC ICM display trace


You can always see the current trace level by right clicking the ICM and selecting "Parameters...", or actually right clicking the ICM node further up the tree and selecting "Parameters...":

Image may be NSFW.
Clik here to view.
SAP MC show ICM parameters


The current trace level is shown as the "rdisp/TRACE" parameter value on the Dispatcher tab:

Image may be NSFW.
Clik here to view.
SAP MC rdisp/TRACE ICM


Again, right clicking the actual ICM node in the tree gives you direct access to the ICM management web page:

Image may be NSFW.
Clik here to view.
MWSnap106 2014-07-23, 09_39_44


From the ICM management page, you can also see the current trace level:

Image may be NSFW.
Clik here to view.
ICM Manager Monitor


Good luck with your tracing.

SAP FRP Data Environment Locations

The locations on disk where the SAP FRP data environments are stored, are administered through transaction /FRE/FRP_ADMIN.

However, if you don't have authorisation for this transaction, you can also check the table "/fre/frpmid_srv" (a client dependent table).

Finding Your SAP F&R Version

The Forecasting & Replenishment offering from SAP runs on SAP SCM.
SAP originally bought the F&R binary calculation engine from a Swiss company called SAF.  This was integrated to the SCM platform and is called through an RFC connection.

If you're planning an upgrade you need to easily identify which version you're running.

There are two areas to check:
- The SAP SCM version.
- The SAF (FRP) binary version.

Check in SPAM (or in SAP GUI, System -> Status -> Component Version) for the SAP SCM Server version.

Note that:
SCM 7.02 (EHP 2) = F&R 5.2.
SCM 7.01 (EHP 1) = F&R 5.1.

Checking the SAF binary must be done at the operating system level.
The usual location is either “/usr/sap/<SID>/SYS/global/frp/bin" or "/usr/sap/<SID>/FRP/bin".

As the <sid>adm user simply call the "safcnfg" binary with the "-version" command line option:

Image may be NSFW.
Clik here to view.
SAP F&R SAF binary version
  

See SAP note 1487615 for details on where to find FRP binary patches.

Finally, you should note that SAP SCM is itself a Business Suite software package like SAP ERP.  Therefore, it is not classed as a HUB or Sidecar landscape pattern, but instead, a source business system.  This means that there is no real dependency link to the SAP ERP version (providing you're on the same technology platform level e.g. 7.31). 
You do need to consider that the interface from ERP to F&R may need some notes applying, some of which may be better implemented through an SPS upgrade instead of notes upon notes. 

See application component SCM-FRE-ERP.

IBM DB2 10.1 Statistics Replication

Scenario: You would like to test a new index in an IBM DB2 10.1 database.  Unfortunately your development system doesn't have anywhere near the same number of records in the database table on which you are creating the new index.

Like many other RDBMSs, DB2 uses table and column statistics to influence the optimiser's decision as to which access path to choose at execution time.
By replicating only the statistics, it's possible to fool the optimiser into thinking the table has more records present, than it really does.  This means that it's likely to choose a different access path.

When performance tuning a database, it's useful to use this method of fooling the optimiser, because you can emulate a larger table in a small development system with little actual data.

The process in DB2 is like this:
- Generate (or export) the statistics for a table in a production database system (PRD) schema DBA.
- Modify the export file.
- Upload the contents of the export file into a development database system (DEV) schema DBB.
- Test.

Step 1 - Export the statistics for a table in production.

Connect into the production database (DBA), then use the db2look command to create an export file containing the UPDATE commands for adjusting the statistics:

db2prd> db2 connect to PRD

db2prd> db2look -d PRD -e -c -m -r -t DBA.TABLE1 -o table1_STATS.sql

The output will be written to the table1_STATS.sql file in the current directory.

Step 2 - Modify the export file.
You should edit the output file to remove all lines before the line“-- Mimic table TABLE1”, failure to do this could mean dropping the TABLE1 table in development.

You must also edit the file and replace all instances of schema “DBA” with “DBB” to ensure that the correct development database schema is found.
The modified file will look like:

-- Mimic table TABLE1

UPDATE SYSSTAT.TABLES
SET CARD=2341434,
NPAGES=14636,
FPAGES=14645,
OVERFLOW=9473,
ACTIVE_BLOCKS=0
WHERE TABNAME = 'TABLE1' AND TABSCHEMA = 'DBB';

UPDATE SYSSTAT.COLUMNS
SET COLCARD=1,
NUMNULLS=0,
...

Step 3 - Upload the statistics into the development database.

db2dev> db2 connect to DEV
db2dev> db2 -tf ikpf_STATS.sql
...
DB20000I The SQL command completed successfully.

DB20000I The SQL command completed successfully.

SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. SQLSTATE=02000

SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. SQLSTATE=02000

You're now ready to test.

To reset (re-gather) the statistics on the development database table, you simply need to re-collect statistics using the RUNSTATS command: "db2 RUNSTATS ON TABLE TABLE1 WITH DISTRIBUTION AND INDEXES ALL".



SAP Kernel to EXT or not to EXT...

Scenario: You're at the point where you are installing a new system and your choice of Kernel is down to the EXT version or the non-EXT version.  Which version should you use?

The difference between the EXT version of a Kernel and the non-EXT version of a Kernel, is simply down to the version of the compiler and compilation Operating System used by SAP to compile the Kernel binaries.

As an example, the 7.21 kernel could be compiled on Windows 2003 Server, using the Visual Studio 2010 compiler.
The 7.20EXT kernel could be compiled on Windows 2003 Server, using the Visual Studio 2012 compiler.

The difference is all about the compilation environment, and nothing to do with functionality.  Or is it...
If you look at SAP note 1926209 - "Questions on subjects of 7.20/7.21 EXT kernel and C/C++ runtime", this would seem to be the case.
However, read SAP notes 1756204 - "Activate fast polling"  and 1728283 - "SAP Kernel 721: General Information" , you will see that it seems to suggest that SAP can and will change the functionality between an EXT and non-EXT kernel (7.21 is used as the example here).
So, be wary and always read up about the benefits of each Kernel, whether EXT or not.

Secure ReadyNAS Duo (v1) ADMIN Share

If you have a ReadyNAS Duo and you're happy with your setup and are now sharing your shares out through your router over the internet, you need to be aware that any old hacker can try and access your ADMIN share (e.g. https://<your-readynas>/admin).

I use mine in exactly that way but don't want Mr A.Hacker trying out a myriad of passwords on my ADMIN share just because my public shares have "Netgear ReadyNAS" plastered all over the front page (a tip for another day I feel).

Instead, if you're comfortable using SSH, (there is a way to do this by using the FrontView config backup, edit the file and put back in place) then you can edit your Apache httpd.conf configuration file so that access to the ADMIN share is restricted to a host or hosts on your local home network only.

Steps:

1, Log into your readynas via SSH as root.
2, Backup your old config file:

# cp -p /etc/frontview/apache/httpd.conf  /etc/frontview/apache/httpd.conf.bak

3, Use 'vi' to edit the httpd.conf:

# vi /etc/frontview/apache/httpd.conf

4, Change the sections as follows:

<Location /admin>
DirectoryIndex index.html
Options ExecCGI
AuthType Basic
AuthName "Control Panel"
require user admin

# block external admin.
Order Deny,Allow
Deny from all
Allow from 192.168 <<< INSERT YOUR LOCAL NETWORK IP ADDRESS SUBNET HERE
</Location>

and

<Location /get_handler>
SetHandler perl-script
PerlHandler get_handler
PerlSendHeader On
Options ExecCGI

# Order allow,deny
# Allow from all
AuthType Basic
AuthName "Control Panel"
require user admin

# block external admin.
Order Deny,Allow
Deny from all
Allow from 192.168 <<< INSERT YOUR LOCAL NETWORK IP ADDRESS SUBNET HERE
</Location>

plus

<Location /dir_list>
AuthType Basic
AuthName "Control Panel"
require user admin
Options ExecCGI
#Allow from all

Order Deny,Allow
Deny from all
Allow from 192.168 <<<-- Insert your subnet here.
</Location>

5, Save the changes with:

<shift + 'ZZ'>

6, Restart your readynas:

# shutdown -r now

7, Test from your local network that you can access the ADMIN share:

https://<readynas IP>/admin

8, Test from the internet that you can't access the ADMIN share:

https://<ISP IP>/admin

You should see a HTTP 403 FORBIDDEN error.

That's it.
If you made an error, you can restore your config from the backup file you took:

# cp -p /etc/frontview/apache/httpd.conf.bak /etc/frontview/apache/httpd.conf

and then restart your readynas.
Don't forget to check the config after you make any changes to shares / firmware etc.

Netgear ReadyNAS Duo Samba Performance

If you access your Netgear ReadyNAS Duo via your Windows Explorer and you think it's a little slow when reading/writing, then you could get a little speed boost from adding one line to the Samba configuration.

You'll need access as root via SSH to log into the ReadyNAS.

Backup the current config file:

# cp -p /etc/samba/smb.conf  /etc/samba/smb.conf.bak

Then as root, edit the samba config file:

# vi /etc/samba/smb.conf

Add the new line under section "[global]":

socket options = TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=65536 SO_SNDBUF=65536

Save the file (ZZ).

Restart Samba:

# /etc/init.d/samba restart

Stopping Samba daemons: nmbd smbd.
Starting Samba daemons: nmbd smbd.

You should notice a performance improvement when transferring files.

Power Notes Searcher Updated to v1.2

First off, a Happy New Year to you!

During the festivities, I've managed to squeeze in a little time to make a couple of modifications to the Power Notes Searcher.  My free Google Chrome Extension for searching for SAP notes and helping you organise the SAP notes you find.

The changes include 1 fix to the Google Font API link which was no longer working due to Google's move to ensure that it's infrastructure is accessed via HTTPS.
I've also included some enhancements to the list of hard SAP links to useful areas of the SAP support site, such as the Support Package Stack schedule and the SL Toolset (for SUM and SWPM).
I also adjusted the link to the SWDC so that it now uses the new support.sap.com site (instead of the old service.sap.com).

As a bonus, I increased the number of note tabs that can be opened in one go, from 5 to 10.
This means that entering 10 SAP notes into the search box (or omni box), will open all of them.
Finally, I increased the number of allowed SAP notes in the history, from 50 to 100.
I found that on a regular project for 1 system installation, I was regularly exceeding the 50 note limit.

With regards to up & coming features, well I originally meant to include the ability to tag notes listed in the history.  This would provide a way of saving favourite notes, plus saving specific notes as a project set or collection.
The initial core code for storing tags against notes is already built in, I just need to spend some time around the peripheral code creating the interface etc.  Let's hope I can get this done in the next few months.

In case you've not installed the extension, you can see the details here on the Google Chrome Extensions Web Store (or from the link on my initial blog post here).

Drop a SAP DB2 10.1 Database & Remove Instance

In case you have installed an IBM DB2 database instance using the SAP Software Provisioning Manager, and you would now like to remove this database and the DB2 software installation (SAP DB instance), then here's a quick method:

As db2<sid>:

db2<sid> # db2stop
db2<sid> # db2 drop database

Then as root:

# cd /db2/db2<sid>/db2_software/instance
# ./db2idrop db2<sid>

# cd /db2/db2<sid>/db2_software/install
# ./db2_deinstall -a

Finally, remove DB related directories (if necessary):

# rm -rf /db2/<SID>/db2dump/*
# rm -rf /db2/<SID>/log_dir/*
# rm -rf /db2/<SID>/*archlogs/*

Deploying SAP Portal JDBC Adapter SDA using Telnet

Once you've built a new com.sap.aii.adapter.lib.sda file ready for deployment into the SAP AS Java, you have two options for deployment in a NW 731 system:
- Deploy using SUM
- Deploy using Telnet

The SUM method can be arduous, taking anywhere from 20 minutes to over a couple of hours if you don't already have the software.
Using Telnet is the lowest common denominator, uses existing software already installed and can take as little as 5-10 minutes.
As the <sid>adm user on the AS Java application server, use Telnet (must be installed on the Unix, Linux or Windows O/S, to perform the deployment:

sidadm> telnet 127.0.0.1 5<##>08
Administrator
<pw>

Once connected, you can query the version of the SDA: 

explore name=com.sap.aii.adapter.lib

Development Component:
name: [com.sap.aii.adapter.lib], vendor: [sap.com], location: [SAP AG], version: [7.3113.20140808110905.0000], software type: [primary-library], csn component: [BC-XI-CON-AFW], dependencies: [[name: 'engine.j2ee14.facade', vendor: 'sap.com']], archive type: [DC]

Then deploy your new SDA file:

deploy /tmp/sap.com_com.sap.aii.adapter.lib  on_deploy_error=stop version_rule=all

Notice that you are disconnected from Telnet at this point.
You should wait around 5-10mins, then re-connect into Telnet as Administrator, then run get_result:

> get_result
The status of the deployed SDUs is Success.
Additional information:

[sdu id: [sap.com_com.sap.aii.adapter.lib]
sdu file path:
/usr/sap/<SID>/J##/j2ee/cluster/server1/temp/tc~bl~deploy_controller/archives/124/1416245030759972000/com.sap.aii.adapter.lib.sda]
version status: [SAME]
deployment status: [Success]
description: []

That's it.





Starting and Stopping Individual SAP AS Java Server Processes

Should you need to restart either manually or automatically (via script) a specific AS Java server node, it's possible to use the sapcontrol tool to provide precise control.
The method detailed below is based on the documentation provided here:

Performs a given control function (EnableProcess/StartProcess, DisableProcess/StopProcess,
SoftStopProcess, ActivateProcess, DeactivateProcess, RestartProcess, SoftRestartProcess,
DumpStackTrace, EnableDebugging, DisableDebugging, IncrementTrace, DecrementTrace) on a given AS
Java process. processname must match a process name in the AS Java process list returned by
J2EEGetProcessList. To perform AS Java instance-wide operations (StartInstance, StopInstance,
RestartInstance, BootInstance, RebootInstance), use processname “all”.
Based on the above information, we can query the AS Java processes for a specific AS Java instance, using the following command line as the sidadm user:

sidadm> sapcontrol -host [host] -nr [sys##] -function J2EEGetProcessList

You should substitute the "host" and "sys##" with the host of your application server (virtual host) and the SAP instance number.
The command will return a list of the processes that the instance is running.
For a 2 node AS Java system, you can see the Server0 and Server1 processes in the list.
To restart Server0 on the AS Java instance use the below command line as the sidadm user:

sidadm> sapcontrol -host [host] -nr [sys##] -function J2EEControlProcess "server0""RestartProcess"

You should substitute the "host" and "sys##" with the host of your application server (virtual host) and the SAP instance number.
Change "server0" for "server1" if you want to restart server1.
Based on the above restart, you can monitor the restart by using the below command line to see when the restart is completed:
sidadm> sapcontrol -host [host] -nr [sys##] -function J2EEGetProcessList | awk -F, '{ if ( $2 != ""&& FNR > 5 ){ print "Name: "$2"\t\tState: "$8"\t\tStarted: "$9 }}'

You should substitute the "host" and "sys##" with the host of your application server (virtual host) and the SAP instance number.

Testing SAP BI Java Query Outside iView

To test a SAP BI Java query outside of an Enterprise Portal iView call, you can use the URL:

http://[EP Server]/irj/servlet/prt/portal/prtroot/pcd!3aportal_content!2fcom.sap.pct!2fplatform_add_ons!2fcom.sap.ip.bi!2fiViews!2fcom.sap.ip.bi.bex?TEMPLATE=TEMP1

You should substitute "[EP Server]" with your SAP Enterprise Portal server hostname, and change "TEMP1" to be the name of the BEx query you wish to run.  You should take the same name as that used in the iView.

SAP PO 7.31 SPS14 JCO NoSuchMethodError

Scenario: We had an outbound interface from SAP PO which was an iDoc being sent to a SAP ABAP stack via HTTP, in the message monitoring log we saw:

javax.ejb.EJBException: nested exception is:
java.lang.RuntimeException:
java.lang.NoSuchMethodError:
com.sap.conn.jco.rt.ClientConnection.execute(Lcom/sap/conn/jco/JCoFunction;Ljava/lang/String;Ljava/lang/String;Lcom/sap/conn/jco/JCoRepository;)V

The message is retried and eventually set to NDLG.

The PO system was a fresh install of PO 7.31, then patched to base SPS13, then we had applied the base 7.31 SPS14 along with the available patches at the time.
During the error, we had the following component levels:

Name    Vendor Location               Version
AJAX-RUNTIME                sap.com               SAP AG 1000.7.31.14.1.20141205212300
BASETABLES       sap.com               SAP AG 1000.7.31.14.0.20141003215100
BI-BASE-B            sap.com               SAP AG 1000.7.31.14.0.20141004141500
BI-BASE-E            sap.com               SAP AG 1000.7.31.14.0.20141004141500
BI-BASE-S            sap.com               SAP AG 1000.7.31.14.0.20141004141500
BI-WDALV           sap.com               SAP AG 1000.7.31.14.0.20141003224600
BI-WDEXT            sap.com               SAP AG 1000.7.31.14.1.20141203045500
BI_UDI  sap.com               SAP AG 1000.7.31.14.0.20141004131400
BPEM-ACC          sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-BASE        sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-BASIS       sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-BUILDT    sap.com               SAP AG 1000.7.31.14.0.20141115023900
BPEM-COLLAB   sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-CONTENT               sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-CORE       sap.com               SAP AG 1000.7.31.14.1.20141203045700
BPEM-CUUI        sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-FACADE  sap.com               SAP AG 1000.7.31.14.1.20141203045700
BPEM-HIM          sap.com               SAP AG 1000.7.31.14.1.20141203045700
BPEM-MM          sap.com               SAP AG 1000.7.31.14.1.20141203012000
BPEM-MON       sap.com               SAP AG 1000.7.31.14.0.20141004155400
BPEM-PP             sap.com               SAP AG 1000.7.31.14.1.20141129055000
BPEM-WDUI      sap.com               SAP AG 1000.7.31.14.1.20141129055000
BRMS-BASE        sap.com               SAP AG 1000.7.31.14.0.20141004130900
BRMS-BUILDT    sap.com               SAP AG 1000.7.31.14.0.20141004130900
BRMS-CORE       sap.com               SAP AG 1000.7.31.14.0.20141004155400
BRMS-FACADE  sap.com               SAP AG 1000.7.31.14.0.20141004155400
BRMS-MON       sap.com               SAP AG 1000.7.31.14.0.20141004155400
BRMS-WDUI      sap.com               SAP AG 1000.7.31.14.0.20141004155400
CAF        sap.com               SAP AG 1000.7.31.14.0.20141004102100
CAF-MF                sap.com               SAP AG 1000.7.31.14.0.20141004102100
CAF-UI  sap.com               SAP AG 1000.7.31.14.0.20141004160600
CFG_ZA                sap.com               SAP AG 1000.7.31.14.0.20141003215100
CFG_ZA_CE        sap.com               SAP AG 1000.7.31.14.0.20141003215100
COLLAB-ADP      sap.com               SAP AG 1000.7.31.14.0.20141004155400
COMP_BUILDT  sap.com               SAP AG 1000.7.31.14.0.20141004104600
CORE-TOOLS      sap.com               SAP AG 1000.7.31.14.1.20141205212100
CU-BASE-JAVA  sap.com               SAP AG 1000.7.31.14.0.20141004130900
CU-BASE-WD     sap.com               SAP AG 1000.7.31.14.0.20141004155400
CU-BASE-WD-EXT            sap.com               SAP AG 1000.7.31.14.0.20141004155400
CU-WD4VC-ADPT            sap.com               SAP AG 1000.7.31.14.0.20141004155400
DATA-MAPPING              sap.com               SAP AG 1000.7.31.14.0.20141004130900
DI_CLIENTS         sap.com               SAP AG 1000.7.31.14.1.20141202210100
ECM-ADMIN      sap.com               SAP AG 1000.7.31.14.0.20141003223800
ECM-APPS          sap.com               SAP AG 1000.7.31.14.0.20141003223800
ECM-CORE          sap.com               SAP AG 1000.7.31.14.0.20141003223800
ECM-JEE-COMP                sap.com               SAP AG 1000.7.31.14.0.20141003223800
ECM-STORE        sap.com               SAP AG 1000.7.31.14.0.20141003223800
ENGFACADE       sap.com               SAP AG 1000.7.31.14.1.20141209182400
ENGINEAPI         sap.com               SAP AG 1000.7.31.14.1.20141130172400
EP-ADMIN          sap.com               SAP AG 1000.7.31.14.1.20141129031400
EP-BASIS              sap.com               SAP AG 1000.7.31.14.2.20141206030600
EP-BASIS-API     sap.com               SAP AG 1000.7.31.14.1.20141130182700
EP-CONNECTIVITY           sap.com               SAP AG 1000.7.31.14.0.20141004132200
EP-MODELING  sap.com               SAP AG 1000.7.31.14.0.20141004132200
EP-RUNTIME      sap.com               SAP AG 1000.7.31.14.0.20141014084300
EP_BUILDT          sap.com               SAP AG 1000.7.31.14.0.20141004104600
ESCONF_BUILDT              sap.com               SAP AG 1000.7.31.14.0.20141004104600
ESI-UI    sap.com               SAP AG 1000.7.31.14.0.20141004155100
ESMP_BUILDT   sap.com               SAP AG 1000.7.31.14.1.20141126014600
ESP_FRAMEWORK          sap.com               SAP AG 1000.7.31.14.0.20141004125900
ESREG-BASIC     sap.com               SAP AG 1000.7.31.14.0.20141004125900
ESREG-SERVICES              sap.com               SAP AG 1000.7.31.14.0.20141004125900
FP-INFRA             sap.com               SAP AG 1000.7.31.14.0.20141003215100
FRAMEWORK    sap.com               SAP AG 1000.7.31.14.0.20141003215100
FRAMEWORK-EXT           sap.com               SAP AG 1000.7.31.14.1.20141206024600
GWJPO sap.com               SAP AG 1000.7.31.14.0.20141004125900
INTG_VIS            sap.com               SAP AG 1000.7.31.14.0.20141004125900
INTG_VIS_DCJ  sap.com               SAP AG 1000.7.31.14.0.20141004125900
J2EE-APPS           sap.com               SAP AG 1000.7.31.14.1.20141125210900
J2EE-FRMW        sap.com               SAP AG 1000.7.31.14.1.20141202210000
JSPM     sap.com               SAP AG 1000.7.31.14.0.20141008191000
KM-KW_JIKS      sap.com               SAP AG 1000.7.31.14.0.20141004131400
LM-CORE             sap.com               SAP AG 1000.7.31.14.2.20141202210000
LM-CTS sap.com               SAP AG 1000.7.31.14.0.20141003224600
LM-CTS-UI          sap.com               SAP AG 1000.7.31.14.0.20141003215100
LM-MODEL-BASE             sap.com               SAP AG 1000.7.31.14.0.20141003224600
LM-MODEL-NW                sap.com               SAP AG 1000.7.31.14.0.20141003224600
LM-SLD sap.com               SAP AG 1000.7.31.14.2.20141202215500
LM-TOOLS           sap.com               SAP AG 1000.7.31.14.0.20141004160100
LMCFG sap.com               SAP AG 1000.7.31.14.1.20141125220400
LMCTC  sap.com               SAP AG 1000.7.31.14.0.20141015180600
LMNWABASICAPPS        sap.com               SAP AG 1000.7.31.14.1.20141128223100
LMNWABASICCOMP      sap.com               SAP AG 1000.7.31.14.1.20141128223100
LMNWABASICMBEAN   sap.com               SAP AG 1000.7.31.14.1.20141209214200
LMNWACDP       sap.com               SAP AG 1000.7.31.14.1.20141128221700
LMNWATOOLS  sap.com               SAP AG 1000.7.31.14.0.20141004160100
LMNWAUIFRMRK            sap.com               SAP AG 1000.7.31.14.1.20141128221700
MESSAGING      sap.com               SAP AG 1000.7.31.14.1.20141206023400
MMR_SERVER   sap.com               SAP AG 1000.7.31.14.0.20141004131400
MOIN_BUILDT  sap.com               SAP AG 1000.7.31.14.0.20141003215000
NWTEC sap.com               SAP AG 1000.7.31.14.0.20141003224600
ODATA-CXF-EXT               sap.com               SAP AG 1000.7.31.14.0.20141003224600
PI-SCP-BUILDT  sap.com               SAP AG 1000.7.31.14.1.20141203013100
PI-SCP-EXT          sap.com               SAP AG 1000.7.31.14.0.20141004125900
PIB2BAS2             sap.com               SAP AG 1000.1.0.4.0.20140929051300
PIB2BOFTP          sap.com               SAP AG 1000.1.0.4.0.20140929051300
PIB2BPGP            sap.com               SAP AG 1000.1.0.4.0.20140929051300
PIB2BSFTP           sap.com               SAP AG 1000.1.0.4.6.20141210073300
PIB2BTOOLKIT   sap.com               SAP AG 1000.1.0.4.1.20141001051200
PIB2BX400           sap.com               SAP AG 1000.1.0.4.0.20140929051300
SAP-XI3RDPARTY             sap.com               SAP AG 1000.7.31.1.0.20110918004000
SAP_BUILDT       sap.com               SAP AG 1000.7.31.14.0.20141003215000
SAP_XIADMIN  sap.com               SAP AG 1000.7.31.14.0.20141004155100
SAP_XIAF            sap.com               SAP AG 1000.7.31.14.2.20141210010200
SAP_XIESR          sap.com               SAP AG 1000.7.31.14.0.20141004125900
SAP_XIGUILIB   sap.com               SAP AG 1000.7.31.14.1.20141201183500
SAP_XITOOL      sap.com               SAP AG 1000.7.31.14.1.20141203013100
SEA-CORE            sap.com               SAP AG 1000.7.31.14.1.20141126010300
SEA-FACADE      sap.com               SAP AG 1000.7.31.14.0.20141004114500
SEA-UI  sap.com               SAP AG 1000.7.31.14.0.20141004144200
SECURITY-EXT    sap.com               SAP AG 1000.7.31.14.0.20141003215100
SERVERCORE      sap.com               SAP AG 1000.7.31.14.1.20141209203300
SERVICE-COMP sap.com               SAP AG 1000.7.31.14.0.20141004130900
SOAMON            sap.com               SAP AG 1000.7.31.14.1.20141126050100
SOAMONBASIC                sap.com               SAP AG 1000.7.31.14.2.20141203045500
SR-UI     sap.com               SAP AG 1000.7.31.14.0.20141004155100
SUPPORTTOOLS               sap.com               SAP AG 1000.7.31.14.0.20141003215100
SWLIFECYCL        sap.com               SAP AG 1000.7.31.14.0.20141004160100
THL-CORE            sap.com               SAP AG 1000.7.31.14.0.20141004155400
TM-WLUI             sap.com               SAP AG 1000.7.31.14.1.20141203045700
UDDI     sap.com               SAP AG 1000.7.31.14.0.20141004125900
UISAPUI5_JAVA               sap.com               SAP AG 1000.7.31.14.1.20141202210100
UKMS_JAVA      sap.com               SAP AG 1000.7.31.14.0.20141004155100
UMEADMIN       sap.com               SAP AG 1000.7.31.14.1.20141202215500
UWLJWF              sap.com               SAP AG 1000.7.31.14.0.20141004141500
VCBASE                sap.com               SAP AG 1000.7.31.14.0.20141004124600
VCCORERT          sap.com               SAP AG 1000.7.31.14.0.20141004124600
VCFRAMEWORK               sap.com               SAP AG 1000.7.31.14.0.20141004124600
VCFREESTYLEKIT               sap.com               SAP AG 1000.7.31.14.0.20141004124600
VCKITBI                sap.com               SAP AG 1000.7.31.14.1.20141203045500
VTP_BUILDT       sap.com               SAP AG 1000.7.31.14.0.20141004104600
WD-ADOBE         sap.com               SAP AG 1000.7.31.14.0.20141003222100
WD-APPS            sap.com               SAP AG 1000.7.31.14.0.20141004101100
WD-FLEX              sap.com               SAP AG 1000.7.31.14.0.20141003222100
WD-RUNTIME   sap.com               SAP AG 1000.7.31.14.2.20141205215300
WD-RUNTIME-EXT          sap.com               SAP AG 1000.7.31.14.0.20141004101100
WDEXTENSIONS               sap.com               SAP AG 1000.7.31.14.1.20141203040100
WSRM  sap.com               SAP AG 1000.7.31.14.0.20141004125900
XI_CNT_SAP_BASIS        sap.com               SAP AG 1000.7.31.14.0.20141004125900


We then downloaded the following patches and applied with SUM:

Name    Version                Current                PatchedLevel
AJAX-RUNTIME                1000.7.31.14.1.20141205212300 1              2
CORE-TOOLS      1000.7.31.14.1.20141205212100 1              2
ENGINEAPI         1000.7.31.14.1.20141130172400 1              2
EP-BASIS              1000.7.31.14.2.20141206030600 2              4
FRAMEWORK    1000.7.31.14.0.20141003215100 0              1
FRAMEWORK-EXT           1000.7.31.14.1.20141206024600 1              2
GWJPO 1000.7.31.14.0.20141004125900 0              1
J2EE-APPS           1000.7.31.14.1.20141125210900 1              4
J2EE-FRMW        1000.7.31.14.1.20141202210000 1              3
LM-CORE             1000.7.31.14.2.20141202210000 2              3
LM-CTS 1000.7.31.14.0.20141003224600 0              1
MESSAGING      1000.7.31.14.3.20150105182200
ODATA-CXF-EXT               1000.7.31.14.0.20141003224600 0              1
SAP_XIAF            1000.7.31.14.4.20150105182200
SAP_XIESR          1000.7.31.14.2.20150107011100
SAP_XIGUILIB   1000.7.31.14.1.20141201183500 1              2
SAP_XITOOL      1000.7.31.14.1.20141203013100 1              2
SERVERCORE      1000.7.31.14.1.20141209203300 1              5
SOAMON            1000.7.31.14.1.20141126050100 1              3
SUPPORTTOOLS               1000.7.31.14.0.20141003215100 0              1
WD-RUNTIME   1000.7.31.14.2.20141205215300 2              4


This fixed the issue.
Viewing all 141 articles
Browse latest View live