mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-11-04 05:12:30 +00:00 
			
		
		
		
	Add perl files of openbmc2
This commit is contained in:
		@@ -16,6 +16,20 @@ use HTTP::Request;
 | 
			
		||||
use HTTP::Headers;
 | 
			
		||||
use HTTP::Cookies;
 | 
			
		||||
use Data::Dumper;
 | 
			
		||||
use Time::HiRes qw(sleep time);
 | 
			
		||||
use JSON;
 | 
			
		||||
use File::Path;
 | 
			
		||||
use Fcntl ":flock";
 | 
			
		||||
use IO::Socket::UNIX qw( SOCK_STREAM );
 | 
			
		||||
use xCAT_monitoring::monitorctrl;
 | 
			
		||||
 | 
			
		||||
my $LOCK_DIR = "/var/lock/xcat/";
 | 
			
		||||
my $LOCK_PATH = "/var/lock/xcat/agent.lock";
 | 
			
		||||
my $AGENT_SOCK_PATH = "/var/run/xcat/agent.sock";
 | 
			
		||||
my $PYTHON_LOG_PATH = "/var/log/xcat/agent.log";
 | 
			
		||||
my $MSG_TYPE = "message";
 | 
			
		||||
my $DB_TYPE = "db";
 | 
			
		||||
my $lock_fd;
 | 
			
		||||
 | 
			
		||||
my $header = HTTP::Headers->new('Content-Type' => 'application/json');
 | 
			
		||||
 | 
			
		||||
@@ -43,4 +57,140 @@ sub send_request {
 | 
			
		||||
    return $id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# if lock is released unexpectedly, python side would aware of the error after
 | 
			
		||||
# getting this lock
 | 
			
		||||
sub acquire_lock {
 | 
			
		||||
    mkpath($LOCK_DIR);
 | 
			
		||||
    # always create a new lock file
 | 
			
		||||
    unlink($LOCK_PATH);
 | 
			
		||||
    open($lock_fd, ">>", $LOCK_PATH) or return undef;
 | 
			
		||||
    flock($lock_fd, LOCK_EX) or return undef;
 | 
			
		||||
    return $lock_fd;
 | 
			
		||||
}
 | 
			
		||||
sub start_python_agent {
 | 
			
		||||
    my $agent_file = "/opt/xcat/lib/python/agent/agent.py";
 | 
			
		||||
    if (! -e $agent_file) {
 | 
			
		||||
        xCAT::MsgUtils->message("S", "Error: '/opt/xcat/lib/python/agent/agent.py' does not exist");
 | 
			
		||||
        return undef;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!defined(acquire_lock())) {
 | 
			
		||||
        xCAT::MsgUtils->message("S", "Error: Faild to require lock");
 | 
			
		||||
        return undef;
 | 
			
		||||
    }
 | 
			
		||||
    my $fd;
 | 
			
		||||
    open($fd, '>', $AGENT_SOCK_PATH) && close($fd);
 | 
			
		||||
    my $pid = fork;
 | 
			
		||||
    if (!defined $pid) {
 | 
			
		||||
        xCAT::MsgUtils->message("S", "Error: Unable to fork process");
 | 
			
		||||
        return undef;
 | 
			
		||||
    }
 | 
			
		||||
    $SIG{CHLD} = 'DEFAULT';
 | 
			
		||||
    if (!$pid) {
 | 
			
		||||
        # child
 | 
			
		||||
        open($fd, ">>", $PYTHON_LOG_PATH) && close($fd);
 | 
			
		||||
        open(STDOUT, '>>', $PYTHON_LOG_PATH) or die("open: $!");
 | 
			
		||||
        open(STDERR, '>>&', \*STDOUT) or die("open: $!");
 | 
			
		||||
        my $ret = exec ("/opt/xcat/lib/python/agent/agent.py");
 | 
			
		||||
        if (!defined($ret)) {
 | 
			
		||||
            xCAT::MsgUtils->message("S", "Error: Failed to start python agent");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return $pid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub handle_message {
 | 
			
		||||
    my ($data, $callback) = @_;
 | 
			
		||||
    if($data->{type} eq $MSG_TYPE) {
 | 
			
		||||
        my $msg = $data->{msg};
 | 
			
		||||
        if ($msg->{type} eq 'info') {
 | 
			
		||||
            xCAT::MsgUtils->message("I", { data => [$msg->{data}] }, $callback);
 | 
			
		||||
        } elsif ($msg->{type} eq 'warning') {
 | 
			
		||||
            xCAT::MsgUtils->message("W", { data => [$msg->{data}] }, $callback);
 | 
			
		||||
        } elsif ($msg->{type} eq 'error'){
 | 
			
		||||
            xCAT::MsgUtils->message("E", { data => [$msg->{data}] }, $callback);
 | 
			
		||||
        } elsif ($msg->{type} eq 'syslog'){
 | 
			
		||||
            xCAT::MsgUtils->message("S", $msg->{data});
 | 
			
		||||
        }
 | 
			
		||||
    } elsif ($data->{type} eq $DB_TYPE) {
 | 
			
		||||
        my $attribute = $data->{attribute};
 | 
			
		||||
        if ($attribute->{name} eq 'status' and $attribute->{method} eq 'set' and $attribute->{type} eq 'node') {
 | 
			
		||||
             my %new_status = ($attribute->{value} => [$attribute->{node}]);
 | 
			
		||||
             xCAT_monitoring::monitorctrl::setNodeStatusAttributes(\%new_status, 1)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub submit_agent_request {
 | 
			
		||||
    my ($pid, $req, $nodeinfo, $callback) = @_;
 | 
			
		||||
    my $sock;
 | 
			
		||||
    my $retry = 0;
 | 
			
		||||
    while($retry < 30) {
 | 
			
		||||
        $sock = IO::Socket::UNIX->new(Peer => $AGENT_SOCK_PATH, Type => SOCK_STREAM, Timeout => 10, Blocking => 1);
 | 
			
		||||
        if (!defined($sock)) {
 | 
			
		||||
            sleep(0.1);
 | 
			
		||||
        } else {
 | 
			
		||||
            last;
 | 
			
		||||
        }
 | 
			
		||||
        $retry++;
 | 
			
		||||
    }
 | 
			
		||||
    if (!defined($sock)) {
 | 
			
		||||
        xCAT::MsgUtils->message("E", { data => ["Failed to connect to the agent"] }, $callback);
 | 
			
		||||
        kill('TERM', $pid);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    my $xcatdebugmode = 0;
 | 
			
		||||
    if ($::XCATSITEVALS{xcatdebugmode}) { $xcatdebugmode = $::XCATSITEVALS{xcatdebugmode} }
 | 
			
		||||
    my %env_hash = ();
 | 
			
		||||
    $env_hash{debugmode} = $xcatdebugmode;
 | 
			
		||||
    my ($data, $sz, $ret, $buf);
 | 
			
		||||
    $data->{module} = 'openbmc';
 | 
			
		||||
    $data->{command} = $req->{command}->[0];
 | 
			
		||||
    $data->{args} = $req->{arg};
 | 
			
		||||
    $data->{cwd} = $req->{cwd};
 | 
			
		||||
    $data->{nodes} = $req->{node};
 | 
			
		||||
    $data->{nodeinfo} = $nodeinfo;
 | 
			
		||||
    $data->{envs} = \%env_hash;
 | 
			
		||||
    $buf = encode_json($data);
 | 
			
		||||
    $sz = pack('i', length($buf));
 | 
			
		||||
    $ret = $sock->send($sz);
 | 
			
		||||
    if (!$ret) {
 | 
			
		||||
        xCAT::MsgUtils->message("E", { data => ["Failed to send message to the agent"] }, $callback);
 | 
			
		||||
        $sock->close();
 | 
			
		||||
        kill('TERM', $pid);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    $ret = $sock->send($buf);
 | 
			
		||||
    if (!$ret) {
 | 
			
		||||
        xCAT::MsgUtils->message("E", { data => ["Failed to send message to the agent"] }, $callback);
 | 
			
		||||
        $sock->close();
 | 
			
		||||
        kill('TERM', $pid);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    while(1) {
 | 
			
		||||
        $ret = $sock->recv($buf, 4);
 | 
			
		||||
        if (!$ret) {
 | 
			
		||||
            last;
 | 
			
		||||
        }
 | 
			
		||||
        $sz = unpack('i', $buf);
 | 
			
		||||
        $ret = $sock->recv($buf, $sz);
 | 
			
		||||
        if (!$ret) {
 | 
			
		||||
            xCAT::MsgUtils->message("E", { data => ["receive data from python agent unexpectedly"] }, $callback);
 | 
			
		||||
            last;
 | 
			
		||||
        }
 | 
			
		||||
        $data = decode_json($buf);
 | 
			
		||||
        handle_message($data, $callback);
 | 
			
		||||
    }
 | 
			
		||||
    # no message received, the socket on the agent side should be closed.
 | 
			
		||||
    $sock->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub wait_agent {
 | 
			
		||||
    my ($pid, $callback) = @_;
 | 
			
		||||
    waitpid($pid, 0);
 | 
			
		||||
    if ($? >> 8 != 0) {
 | 
			
		||||
        xCAT::MsgUtils->message("E", { data => ["python agent exited unexpectedly"] }, $callback);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
 
 | 
			
		||||
@@ -679,6 +679,21 @@ sub preprocess_request {
 | 
			
		||||
    }
 | 
			
		||||
    ##############################################
 | 
			
		||||
 | 
			
		||||
    # Provide a way to change to python code before formal release
 | 
			
		||||
    if (ref($request->{environment}) eq 'ARRAY' and ref($request->{environment}->[0]->{XCAT_OPENBMC_PYTHON}) eq 'ARRAY') {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->[0]->{XCAT_OPENBMC_PYTHON}->[0];
 | 
			
		||||
    } elsif (ref($request->{environment}) eq 'ARRAY') {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->[0]->{XCAT_OPENBMC_PYTHON};
 | 
			
		||||
    } else {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->{XCAT_OPENBMC_PYTHON};
 | 
			
		||||
    }
 | 
			
		||||
    ##############################################
 | 
			
		||||
 | 
			
		||||
    if (defined($::OPENBMC_PYTHON) and $::OPENBMC_PYTHON eq "YES") {
 | 
			
		||||
        $request = {};
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $callback  = shift;
 | 
			
		||||
 | 
			
		||||
    if ($::XCATSITEVALS{xcatdebugmode}) { $xcatdebugmode = $::XCATSITEVALS{xcatdebugmode} }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										229
									
								
								xCAT-server/lib/xcat/plugins/openbmc2.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								xCAT-server/lib/xcat/plugins/openbmc2.pm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,229 @@
 | 
			
		||||
#!/usr/bin/perl
 | 
			
		||||
### IBM(c) 2017 EPL license http://www.eclipse.org/legal/epl-v10.html
 | 
			
		||||
 | 
			
		||||
package xCAT_plugin::openbmc2;
 | 
			
		||||
 | 
			
		||||
BEGIN
 | 
			
		||||
    {
 | 
			
		||||
        $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | 
			
		||||
    }
 | 
			
		||||
use lib "$::XCATROOT/lib/perl";
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings "all";
 | 
			
		||||
 | 
			
		||||
use JSON;
 | 
			
		||||
use Getopt::Long;
 | 
			
		||||
use xCAT::Utils;
 | 
			
		||||
use xCAT::Usage;
 | 
			
		||||
use xCAT::SvrUtils;
 | 
			
		||||
use xCAT::OPENBMC;
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
=head3  handled_commands
 | 
			
		||||
 | 
			
		||||
  Return list of commands handled by this plugin
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
sub handled_commands {
 | 
			
		||||
    return {
 | 
			
		||||
        rpower         => 'nodehm:mgt=openbmc',
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
my %node_info = ();
 | 
			
		||||
my $callback;
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
=head3  preprocess_request
 | 
			
		||||
 | 
			
		||||
  preprocess the command
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
sub preprocess_request {
 | 
			
		||||
    my $request = shift;
 | 
			
		||||
    $callback  = shift;
 | 
			
		||||
 | 
			
		||||
    # if $::OPENBMC_PYTHON is 'YES', will run this script
 | 
			
		||||
    if (ref($request->{environment}) eq 'ARRAY' and ref($request->{environment}->[0]->{XCAT_OPENBMC_PYTHON}) eq 'ARRAY') {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->[0]->{XCAT_OPENBMC_PYTHON}->[0];
 | 
			
		||||
    } elsif (ref($request->{environment}) eq 'ARRAY') {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->[0]->{XCAT_OPENBMC_PYTHON};
 | 
			
		||||
    } else {
 | 
			
		||||
        $::OPENBMC_PYTHON = $request->{environment}->{XCAT_OPENBMC_PYTHON};
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    if (! (defined($::OPENBMC_PYTHON) and $::OPENBMC_PYTHON eq "YES")) {
 | 
			
		||||
        $request = {};
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    my $command   = $request->{command}->[0];
 | 
			
		||||
    my $noderange = $request->{node};
 | 
			
		||||
    my $extrargs  = $request->{arg};
 | 
			
		||||
    my @exargs    = ($request->{arg});
 | 
			
		||||
    my @requests;
 | 
			
		||||
 | 
			
		||||
    if (ref($extrargs)) {
 | 
			
		||||
        @exargs = @$extrargs;
 | 
			
		||||
    }
 | 
			
		||||
    my $usage_string = xCAT::Usage->parseCommand($command, @exargs);
 | 
			
		||||
    if ($usage_string) {
 | 
			
		||||
        $callback->({ data => [$usage_string] });
 | 
			
		||||
        $request = {};
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    my $parse_result = parse_args($command, $extrargs, $noderange);
 | 
			
		||||
    if (ref($parse_result) eq 'ARRAY') {
 | 
			
		||||
        my $error_data;
 | 
			
		||||
        foreach my $node (@$noderange) {
 | 
			
		||||
            $error_data .= "\n" if ($error_data);
 | 
			
		||||
            $error_data .= "$node: Error: " . "$parse_result->[1]";
 | 
			
		||||
        }
 | 
			
		||||
        $callback->({ errorcode => [$parse_result->[0]], data => [$error_data] });
 | 
			
		||||
        $request = {};
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    my $sn = xCAT::ServiceNodeUtils->get_ServiceNode($noderange, "xcat", "MN");
 | 
			
		||||
    foreach my $snkey (keys %$sn) {
 | 
			
		||||
        my $reqcopy = {%$request};
 | 
			
		||||
        $reqcopy->{node}                   = $sn->{$snkey};
 | 
			
		||||
        $reqcopy->{'_xcatdest'}            = $snkey;
 | 
			
		||||
        $reqcopy->{_xcatpreprocessed}->[0] = 1;
 | 
			
		||||
        push @requests, $reqcopy;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return \@requests;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
=head3  process_request
 | 
			
		||||
 | 
			
		||||
  Process the command
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
sub process_request {
 | 
			
		||||
    my $request = shift;
 | 
			
		||||
    $callback = shift;
 | 
			
		||||
    my $noderange = $request->{node};
 | 
			
		||||
    my $check = parse_node_info($noderange);
 | 
			
		||||
    $callback->({ errorcode => [$check] }) if ($check);
 | 
			
		||||
    my $pid = xCAT::OPENBMC::start_python_agent();
 | 
			
		||||
    if (!defined($pid)) {
 | 
			
		||||
        xCAT::MsgUtils->message("E", { data => ["Failed to start python agent"] }, $callback);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    xCAT::OPENBMC::submit_agent_request($pid, $request, \%node_info, $callback);
 | 
			
		||||
    xCAT::OPENBMC::wait_agent($pid, $callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
=head3  parse_args
 | 
			
		||||
 | 
			
		||||
  Parse the command line options and operands
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
sub parse_args {
 | 
			
		||||
    my $command  = shift;
 | 
			
		||||
    my $extrargs = shift;
 | 
			
		||||
    my $noderange = shift;
 | 
			
		||||
    my $subcommand = undef;
 | 
			
		||||
 | 
			
		||||
    if (scalar(@ARGV) != 2 and ($command =~ /rpower/)) {
 | 
			
		||||
        return ([ 1, "Only one option is supported at the same time for $command" ]);
 | 
			
		||||
    } else {
 | 
			
		||||
        $subcommand = $ARGV[0];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($command eq "rpower") {
 | 
			
		||||
        unless ($subcommand =~ /^on$|^off$|^softoff$|^reset$|^boot$|^bmcreboot$|^bmcstate$|^status$|^stat$|^state$/) {
 | 
			
		||||
            return ([ 1, "Unsupported command: $command $subcommand" ]);
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        return ([ 1, "Unsupported command: $command" ]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
=head3  parse_node_info
 | 
			
		||||
 | 
			
		||||
  Parse the node information: bmc, bmcip, username, password
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
#-------------------------------------------------------
 | 
			
		||||
sub parse_node_info {
 | 
			
		||||
    my $noderange = shift;
 | 
			
		||||
    my $rst = 0;
 | 
			
		||||
 | 
			
		||||
    my $passwd_table = xCAT::Table->new('passwd');
 | 
			
		||||
    my $passwd_hash = $passwd_table->getAttribs({ 'key' => 'openbmc' }, qw(username password));
 | 
			
		||||
 | 
			
		||||
    my $openbmc_table = xCAT::Table->new('openbmc');
 | 
			
		||||
    my $openbmc_hash = $openbmc_table->getNodesAttribs(\@$noderange, ['bmc', 'username', 'password']);
 | 
			
		||||
 | 
			
		||||
    foreach my $node (@$noderange) {
 | 
			
		||||
        if (defined($openbmc_hash->{$node}->[0])) {
 | 
			
		||||
            if ($openbmc_hash->{$node}->[0]->{'bmc'}) {
 | 
			
		||||
                $node_info{$node}{bmc} = $openbmc_hash->{$node}->[0]->{'bmc'};
 | 
			
		||||
                $node_info{$node}{bmcip} = xCAT::NetworkUtils::getNodeIPaddress($openbmc_hash->{$node}->[0]->{'bmc'});
 | 
			
		||||
            }
 | 
			
		||||
            unless($node_info{$node}{bmc}) {
 | 
			
		||||
                xCAT::SvrUtils::sendmsg("Error: Unable to get attribute bmc", $callback, $node);
 | 
			
		||||
                $rst = 1;
 | 
			
		||||
                next;
 | 
			
		||||
            }
 | 
			
		||||
            unless($node_info{$node}{bmcip}) {
 | 
			
		||||
                xCAT::SvrUtils::sendmsg("Error: Unable to resolve ip address for bmc: $node_info{$node}{bmc}", $callback, $node);
 | 
			
		||||
                delete $node_info{$node};
 | 
			
		||||
                $rst = 1;
 | 
			
		||||
                next;
 | 
			
		||||
            }
 | 
			
		||||
            if ($openbmc_hash->{$node}->[0]->{'username'}) {
 | 
			
		||||
                $node_info{$node}{username} = $openbmc_hash->{$node}->[0]->{'username'};
 | 
			
		||||
            } elsif ($passwd_hash and $passwd_hash->{username}) {
 | 
			
		||||
                $node_info{$node}{username} = $passwd_hash->{username};
 | 
			
		||||
            } else {
 | 
			
		||||
                xCAT::SvrUtils::sendmsg("Error: Unable to get attribute username", $callback, $node);
 | 
			
		||||
                delete $node_info{$node};
 | 
			
		||||
                $rst = 1;
 | 
			
		||||
                next;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($openbmc_hash->{$node}->[0]->{'password'}) {
 | 
			
		||||
                $node_info{$node}{password} = $openbmc_hash->{$node}->[0]->{'password'};
 | 
			
		||||
            } elsif ($passwd_hash and $passwd_hash->{password}) {
 | 
			
		||||
                $node_info{$node}{password} = $passwd_hash->{password};
 | 
			
		||||
            } else {
 | 
			
		||||
                xCAT::SvrUtils::sendmsg("Error: Unable to get attribute password", $callback, $node);
 | 
			
		||||
                delete $node_info{$node};
 | 
			
		||||
                $rst = 1;
 | 
			
		||||
                next;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            xCAT::SvrUtils::sendmsg("Error: Unable to get information from openbmc table", $callback, $node);
 | 
			
		||||
            $rst = 1;
 | 
			
		||||
            next;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $rst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
		Reference in New Issue
	
	Block a user