<commit_before>#!/usr/bin/ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# 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.                                             #
#--------------------------------------------------------------------------- #

$LOAD_PATH.unshift File.dirname(__FILE__)

require 'yaml'

# Storage mappers
require 'storageutils'
require 'qcow2'
require 'raw'
require 'rbd'

require_relative '../lib/xmlparser'
require_relative '../lib/opennebula_vm'
require_relative '../../scripts_common'

# -----------------------------------------------------------------------------
# This class reads and holds configuration attributes for the LXC driver
# -----------------------------------------------------------------------------
class LXCConfiguration < Hash

    DEFAULT_CONFIGURATION = {
        :vnc => {
            :width   => '800',
            :height  => '600',
            :timeout => '300',
            :command => 'sudo lxc-console'
        },
        :filesystem => 'ext4',
        :datastore_location => '/var/lib/one/datastores',
        :default_lxc_config => '/usr/share/lxc/config/common.conf',
        :id_map => 600100001, # First id for mapping
        :max_map => 65536     # Max id for mapping
    }

    LXCRC = '../../etc/vmm/lxc/lxcrc'

    def initialize
        replace(DEFAULT_CONFIGURATION)

        begin
            merge!(YAML.load_file("#{__dir__}/#{LXCRC}"))
        rescue StandardError => e
            OpenNebula.log_error e
        end

        super
    end

end

# -----------------------------------------------------------------------------
# This class parses and wraps the information in the Driver action data
# -----------------------------------------------------------------------------
class LXCVM < OpenNebulaVM

    CONTAINER_FS_PATH = '/var/lib/lxc-one'

    attr_reader :lxcrc, :bind_folder

    def initialize(xml)
        # Load Driver configuration
        @lxcrc = LXCConfiguration.new

        super(xml, @lxcrc)

        @bind_folder = "#{CONTAINER_FS_PATH}/#{@vm_id}"
    end

    # Returns a Hash representing the LXC configuration for this OpenNebulaVM
    def to_lxc
        prefix = 'lxc'
        lxc    = {}

        # Always include default file with uids mapping
        lxc["#{prefix}.include"] = [@lxcrc[:default_lxc_config]]

        # Add disks
        disks.each do |disk|
            disk.to_lxc.each do |key, val|
                if lxc[key].nil?
                    lxc[key] = [val]
                else
                    lxc[key] << val
                end
            end
        end

        # Add nics
        get_nics.each_with_index do |nic, i|
            lxc["lxc.net.#{i}.type"]      = 'veth'
            lxc["lxc.net.#{i}.link"]      = nic['BRIDGE']
            lxc["lxc.net.#{i}.hwaddr"]    = nic['MAC']
            lxc["lxc.net.#{i}.veth.pair"] = "one-#{@vm_id}-#{nic['NIC_ID']}"
        end

        # Add cgroup limitations
        # rubocop:disable Layout/LineLength
        lxc['lxc.cgroup.cpu.shares']            = cpu_shares
        lxc['lxc.cgroup.memory.limit_in_bytes'] = memmory_limit_in_bytes
        lxc['lxc.cgroup.memory.oom_control']    = 1 # Avoid OOM to kill the process when limit is reached
        # rubocop:enable Layout/LineLength

        # User mapping
        # rubocop:disable Layout/LineLength
        lxc['lxc.idmap'] = ["u 0 #{@lxcrc[:id_map]} #{@lxcrc[:max_map]}",
                            "g 0 #{@lxcrc[:id_map]} #{@lxcrc[:max_map]}"]
        # rubocop:enable Layout/LineLength

        hash_to_lxc(lxc)
    end

    # Returns an Array of Disk objects, each one represents an OpenNebula DISK
    def disks
        adisks = []

        return adisks if @rootfs_id.nil?

        @xml.elements('//TEMPLATE/DISK').each do |xml|
            next if xml['TYPE'].downcase == 'swap'

            if xml['DISK_ID'] == @rootfs_id
                adisks << Disk.new_root(xml, @sysds_path, @vm_id)
            else
                adisks << Disk.new_disk(xml, @sysds_path, @vm_id)
            end
        end

        context_xml = @xml.element('//TEMPLATE/CONTEXT')

        if context_xml
            adisks << Disk.new_context(context_xml, @sysds_path, @vm_id)
        end

        adisks
    end

    private

    # Returns the config in LXC style format
    def hash_to_lxc(hash)
        lxc = ''

        hash.each do |key, value|
            # Process duplicate values from array keys
            if value.class == Array
                value.each do |v|
                    lxc << "#{key} = '#{v}'\n"
                end
            else
                lxc << "#{key} = '#{value}'\n"
            end
        end

        lxc
    end

end

# ------------------------------------------------------------------------------
# Class for managing Container Disks
# ------------------------------------------------------------------------------
class Disk

    attr_accessor :id, :vm_id, :sysds_path

    class << self

        def new_context(xml, sysds_path, vm_id)
            Disk.new(xml, sysds_path, vm_id, false, true)
        end<commit_msg>F #5228: add support for LXC raw section (#923)<commit_after>#!/usr/bin/ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# 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.                                             #
#--------------------------------------------------------------------------- #

$LOAD_PATH.unshift File.dirname(__FILE__)

require 'yaml'

# Storage mappers
require 'storageutils'
require 'qcow2'
require 'raw'
require 'rbd'

require_relative '../lib/xmlparser'
require_relative '../lib/opennebula_vm'
require_relative '../../scripts_common'

# -----------------------------------------------------------------------------
# This class reads and holds configuration attributes for the LXC driver
# -----------------------------------------------------------------------------
class LXCConfiguration < Hash

    DEFAULT_CONFIGURATION = {
        :vnc => {
            :width   => '800',
            :height  => '600',
            :timeout => '300',
            :command => 'sudo lxc-console'
        },
        :filesystem => 'ext4',
        :datastore_location => '/var/lib/one/datastores',
        :default_lxc_config => '/usr/share/lxc/config/common.conf',
        :id_map => 600100001, # First id for mapping
        :max_map => 65536     # Max id for mapping
    }

    LXCRC = '../../etc/vmm/lxc/lxcrc'

    def initialize
        replace(DEFAULT_CONFIGURATION)

        begin
            merge!(YAML.load_file("#{__dir__}/#{LXCRC}"))
        rescue StandardError => e
            OpenNebula.log_error e
        end

        super
    end

end

# -----------------------------------------------------------------------------
# This class parses and wraps the information in the Driver action data
# -----------------------------------------------------------------------------
class LXCVM < OpenNebulaVM

    CONTAINER_FS_PATH = '/var/lib/lxc-one'

    attr_reader :lxcrc, :bind_folder

    def initialize(xml)
        # Load Driver configuration
        @lxcrc = LXCConfiguration.new

        super(xml, @lxcrc)

        @bind_folder = "#{CONTAINER_FS_PATH}/#{@vm_id}"
    end

    # Returns a Hash representing the LXC configuration for this OpenNebulaVM
    def to_lxc
        prefix = 'lxc'
        lxc    = {}

        # Always include default file with uids mapping
        lxc["#{prefix}.include"] = [@lxcrc[:default_lxc_config]]

        # Add disks
        disks.each do |disk|
            disk.to_lxc.each do |key, val|
                if lxc[key].nil?
                    lxc[key] = [val]
                else
                    lxc[key] << val
                end
            end
        end

        # Add nics
        get_nics.each_with_index do |nic, i|
            lxc["lxc.net.#{i}.type"]      = 'veth'
            lxc["lxc.net.#{i}.link"]      = nic['BRIDGE']
            lxc["lxc.net.#{i}.hwaddr"]    = nic['MAC']
            lxc["lxc.net.#{i}.veth.pair"] = "one-#{@vm_id}-#{nic['NIC_ID']}"
        end

        # Add cgroup limitations
        # rubocop:disable Layout/LineLength
        lxc['lxc.cgroup.cpu.shares']            = cpu_shares
        lxc['lxc.cgroup.memory.limit_in_bytes'] = memmory_limit_in_bytes
        lxc['lxc.cgroup.memory.oom_control']    = 1 # Avoid OOM to kill the process when limit is reached
        # rubocop:enable Layout/LineLength

        # User mapping
        # rubocop:disable Layout/LineLength
        lxc['lxc.idmap'] = ["u 0 #{@lxcrc[:id_map]} #{@lxcrc[:max_map]}",
                            "g 0 #{@lxcrc[:id_map]} #{@lxcrc[:max_map]}"]
        # rubocop:enable Layout/LineLength

        # hash_to_lxc values should prevail over raw section values
        hash_to_lxc(parse_raw.merge(lxc))
    end

    # Returns an Array of Disk objects, each one represents an OpenNebula DISK
    def disks
        adisks = []

        return adisks if @rootfs_id.nil?

        @xml.elements('//TEMPLATE/DISK').each do |xml|
            next if xml['TYPE'].downcase == 'swap'

            if xml['DISK_ID'] == @rootfs_id
                adisks << Disk.new_root(xml, @sysds_path, @vm_id)
            else
                adisks << Disk.new_disk(xml, @sysds_path, @vm_id)
            end
        end

        context_xml = @xml.element('//TEMPLATE/CONTEXT')

        if context_xml
            adisks << Disk.new_context(context_xml, @sysds_path, @vm_id)
        end

        adisks
    end

    private

    # Returns the config in LXC style format
    def hash_to_lxc(hash)
        lxc = ''

        hash.each do |key, value|
            # Process duplicate values from array keys
            if value.class == Array
                value.each do |v|
                    lxc << "#{key} = '#{v}'\n"
                end
            else
                lxc << "#{key} = '#{value}'\n"
            end
        end

        lxc
    end

    def parse_raw
        raw_map = {}

        begin
            return raw_map if @xml['//RAW/TYPE'].downcase != 'lxc'

            # only add valid lxc configuration statements
            regex = /^(lxc\.(?:[a-zA-Z0-9]+\.)*[a-zA-Z0-9]+\s*)=(.*)$/

            @xml['//RAW/DATA'].each_line do |l|
                l.strip!

                match = l.match(regex)

                next if match.nil?

                key = match[1].strip
                value = match[2].strip

                if !raw_map[key].nil?
                    raw_map[key] = value
                else
                    raw_map[key] = Array(raw_map[key]) << value
                end
            end
        rescue StandardError
        end

        raw_map
    end

end

# ------------------------------------------------------------------------------
# Class for managing Container Disks
# ------------------------------------------------------------------------------
class Disk

    attr_accessor :id, :vm_id, :sysds_path

    class << self

        def new_context(xml, sysds_path, vm_id)
            Disk.new(xml, sysds_path, vm_id, false, true)
        end
{'fraction_non_alphanumeric': 0.19286871961102106, 'fraction_numerical': 0.00717758740449178, 'mean_word_length': 1.236451501553331, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 32}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3391064', 'n_tokens_mistral': 3658, 'n_tokens_neox': 3464, 'n_words': 876}