summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Berto <463349+robertoberto@users.noreply.github.com>2024-05-15 00:51:46 +0000
committerRoberto Berto <463349+robertoberto@users.noreply.github.com>2024-05-15 00:51:46 +0000
commit1f67acf077af435f9770d58c5331983c00ede7d3 (patch)
treec06d068d99c67aba5eb98307e7345931752f5280
parent8e90d13f6d762bbd0167ddfe26dddd2884ab3dfe (diff)
downloadpacker-vyos-1f67acf077af435f9770d58c5331983c00ede7d3.tar.gz
packer-vyos-1f67acf077af435f9770d58c5331983c00ede7d3.zip
vyos installer in 2 stages image1 and image2
-rw-r--r--Makefile42
-rw-r--r--README.md6
-rw-r--r--example.env17
-rw-r--r--scripts/vyos/cleanup.sh (renamed from scripts/vyos/vyos-install-pre.sh)4
-rw-r--r--scripts/vyos/cloud-init-datasource.sh17
-rw-r--r--scripts/vyos/cloud-init-debian.sh6
-rw-r--r--scripts/vyos/cloud-init-vyos.sh8
-rw-r--r--scripts/vyos/configure.sh24
-rw-r--r--scripts/vyos/grub-serial.sh15
-rw-r--r--scripts/vyos/vyos-install-expect.sh (renamed from scripts/vyos/vyos-install.sh)0
-rwxr-xr-xtools/vnc-connect.sh (renamed from vnc-connect.sh)0
-rw-r--r--vyos-image1.pkr.hcl (renamed from vyos.pkr.hcl)103
-rw-r--r--vyos-image2.pkr.hcl (renamed from vyos-image.pkr.hcl)77
-rw-r--r--vyos.pkrvars.hcl40
14 files changed, 227 insertions, 132 deletions
diff --git a/Makefile b/Makefile
index f40121d..905d398 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#!make
# if not set, set defaults
-PARALLEL_BUILDS ?= 0
+#PARALLEL_BUILDS ?= 0
PACKER_LOG ?= 0
# always use DISPLAY :99
DISPLAY=:99
@@ -11,8 +11,7 @@ DISPLAY=:99
# export all
export
-.PHONY: help build init upgrade clean x11
-
+.PHONY: help
help:
@echo "make working:"
@echo "- will use local.pkrvars.hcl if exists or vyos.pkrvars.hcl"
@@ -32,31 +31,52 @@ help:
# endif
+.PHONY: build1
+build1:
+# if exist local.pkrvars.hcl load it
+ifneq ($(wildcard local.pkrvars.hcl),)
+ packer build \
+ -var-file=local.pkrvars.hcl \
+ -parallel-builds=0 \
+ vyos-image1.pkr.hcl
+else
+ packer build \
+ -var-file=vyos.pkrvars.hcl \
+ -parallel-builds=0 \
+ vyos-image1.pkr.hcl
+endif
-build:
+.PHONY: build2
+build2:
# if exist local.pkrvars.hcl load it
ifneq ($(wildcard local.pkrvars.hcl),)
packer build \
-var-file=local.pkrvars.hcl \
- -parallel-builds=$(PARALLEL_BUILDS) \
- vyos.pkr.hcl
+ -parallel-builds=0 \
+ vyos-image2.pkr.hcl
else
packer build \
-var-file=vyos.pkrvars.hcl \
- -parallel-builds=$(PARALLEL_BUILDS) \
- vyos.pkr.hcl
+ -parallel-builds=0 \
+ vyos-image2.pkr.hcl
endif
+.PHONY: init
init:
- packer init vyos.pkr.hcl
+ packer init vyos-image1.pkr.hcl
+ packer init vyos-image2.pkr.hcl
+.PHONY: upgrade
upgrade:
- packer init -upgrade vyos.pkr.hcl
+ packer init -upgrade vyos-image1.pkr.hcl
+ packer init -upgrade vyos-image2.pkr.hcl
+.PHONY: clean
clean:
- rm -rf output-*
+ rm -rf output/*
# you need to run this first to use headless=false
+.PHONY: x11server
x11server:
Xvfb :99 -screen 0 1024x768x16 &
export DISPLAY=:99
diff --git a/README.md b/README.md
index 54758f4..a630ff6 100644
--- a/README.md
+++ b/README.md
@@ -104,3 +104,9 @@ Use:
# Build
* ```make build```, for build images
+
+# Vars files
+- .env: building vars: control building process
+- vyos.pkrvars.hcl: image vars: define image parameters - git default
+- local.pkrvars.hcl: image vars: define image parameters - clone vyos.pkrvars.hcl to override it locally
+
diff --git a/example.env b/example.env
index 6a9747c..1149b10 100644
--- a/example.env
+++ b/example.env
@@ -1,6 +1,9 @@
-# this env vars are all for development purposes
-# use local.pkrvars.hcl for production
+# vars:
+# - .env building vars: control building process
+# - vyos.pkrvars.hcl image vars: define image parameters - git default
+# - local.pkrvars.hcl image vars: define image parameters - clone vyos.pkrvars.hcl to override it locally
+
# PACKER_LOG=1 will show more packer output (leave 0 for automated packer deployments reduce verbosity)
PACKER_LOG=1
@@ -15,16 +18,18 @@ VNC_PORT_FIXED=5900
HOST_PORT_FIXED=2222
# Seconds before shutdown (put few 300 or 600 if need to access ssh for developing on packer-vyos)
-SLEEP_BEFORE_SHUTDOWN=0
+SLEEP_BEFORE_SHUTDOWN=0
+
+
-# Recommendation for development :
+# Recommendation for development:
# VNC_PORT_FIXED=5900
# HOST_PORT_FIXED=2222
# PACKER_LOG=1
# PARALLEL_BUILDS=1
-# SLEEP_BEFORE_SHUTDOWN=300
+# SLEEP_BEFORE_SHUTDOWN=300
-# Recommendation for production:
+# Recommendation for automated building/production:
# PARALLEL_BUILDS=1
# PACKER_LOG=0
# SLEEP_BEFORE_SHUTDOWN=0
diff --git a/scripts/vyos/vyos-install-pre.sh b/scripts/vyos/cleanup.sh
index c61905d..6ccf753 100644
--- a/scripts/vyos/vyos-install-pre.sh
+++ b/scripts/vyos/cleanup.sh
@@ -13,7 +13,7 @@ cat <<EOF > /home/vyos/cleanup-vyos.sh
#!/bin/vbash
source /opt/vyatta/etc/functions/script-template
configure
-set system host-name host-name 'test'
+set system host-name 'test'
commit
save
exit
@@ -45,6 +45,8 @@ rm -rf /tmp/*
# removing log files
rm -rf /var/log/*
+rm -rf /home/vyos/cleanup-vyos.sh
+
# removing history
export HISTFILE=0
rm -f /home/vyos/.bash_history
diff --git a/scripts/vyos/cloud-init-datasource.sh b/scripts/vyos/cloud-init-datasource.sh
new file mode 100644
index 0000000..66e5509
--- /dev/null
+++ b/scripts/vyos/cloud-init-datasource.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -e
+set -x
+
+if [[ "${CLOUD_INIT}" == "debian" || "${CLOUD_INIT}" == "vyos" ]]; then
+ if [[ "${CLOUD_INIT_DATASOURCE}" == "nocloud_configdrive" ]]; then
+ cat <<EOF > /etc/cloud/cloud.cfg.d/99_nocloud_configdrive.cfg
+datasource_list: [ NoCloud, ConfigDrive ]
+EOF
+ else
+ echo "$0 - info: cloud_init_datasource will not run, not supported cloud_init_datasource"
+ exit 0
+ fi
+else
+ echo "$0 - info: cloud_init_datasource will not run, not supported cloud_init"
+fi
diff --git a/scripts/vyos/cloud-init-debian.sh b/scripts/vyos/cloud-init-debian.sh
index 2cf398f..6a66935 100644
--- a/scripts/vyos/cloud-init-debian.sh
+++ b/scripts/vyos/cloud-init-debian.sh
@@ -21,3 +21,9 @@ apt install -y \
ifupdown
systemctl enable cloud-init
+
+cat <<EOF > /etc/cloud/cloud.cfg.d/99_pve.cfg
+datasource_list: [ NoCloud, ConfigDrive ]
+EOF
+
+
diff --git a/scripts/vyos/cloud-init-vyos.sh b/scripts/vyos/cloud-init-vyos.sh
index 92be545..0dd3d93 100644
--- a/scripts/vyos/cloud-init-vyos.sh
+++ b/scripts/vyos/cloud-init-vyos.sh
@@ -19,5 +19,11 @@ apt install -t "$VYOS_RELEASE" --force-yes -y \
cloud-init \
cloud-utils \
ifupdown
-
systemctl enable cloud-init
+
+
+cat <<EOF > /etc/cloud/cloud.cfg.d/90_disable_config_stage.cfg
+# Disable all config-stage modules
+cloud_config_modules:
+EOF
+
diff --git a/scripts/vyos/configure.sh b/scripts/vyos/configure.sh
new file mode 100644
index 0000000..2628959
--- /dev/null
+++ b/scripts/vyos/configure.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -e
+set -x
+
+# export DEBIAN_FRONTEND=noninteractive
+
+# delete interfaces ethernet eth0 address
+# delete interfaces ethernet eth0 hw-id
+# delete system name-server
+
+cat <<EOF > /home/vyos/configure-vyos.sh
+#!/bin/vbash
+source /opt/vyatta/etc/functions/script-template
+configure
+set system host-name 'test'
+commit
+save
+exit
+EOF
+chmod 0700 /home/vyos/configure-vyos.sh
+chown vyos:users /home/vyos/configure-vyos.sh
+su - vyos -c "/home/vyos/configure-vyos.sh"
+
diff --git a/scripts/vyos/grub-serial.sh b/scripts/vyos/grub-serial.sh
new file mode 100644
index 0000000..b72b1a8
--- /dev/null
+++ b/scripts/vyos/grub-serial.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -e
+set -x
+
+if [[ "${GRUB_SERIAL}" -ne 1 ]]; then
+ echo "$0 - info: grub will keep default=0 (kvm). to use serial add to .env: GRUB_SERIAL=1"
+ exit 0
+fi
+
+GRUB_CFG="/boot/grub/grub.cfg"
+GRUB_DEFAULT="/etc/default/grub"
+
+sed -i 's/^set default=.*/set default=1/' $GRUB_CFG
+sed -i 's/^set default=.*/set default=1/' $GRUB_DEFAULT
diff --git a/scripts/vyos/vyos-install.sh b/scripts/vyos/vyos-install-expect.sh
index aa23064..aa23064 100644
--- a/scripts/vyos/vyos-install.sh
+++ b/scripts/vyos/vyos-install-expect.sh
diff --git a/vnc-connect.sh b/tools/vnc-connect.sh
index abb4f2c..abb4f2c 100755
--- a/vnc-connect.sh
+++ b/tools/vnc-connect.sh
diff --git a/vyos.pkr.hcl b/vyos-image1.pkr.hcl
index 2f7f2dd..98c93fb 100644
--- a/vyos.pkr.hcl
+++ b/vyos-image1.pkr.hcl
@@ -1,3 +1,11 @@
+packer {
+ required_plugins {
+ qemu = {
+ version = "~> 1"
+ source = "github.com/hashicorp/qemu"
+ }
+ }
+}
# dont edit those vars below, customize in local.auto.pkrvars.hcl using local.example.pkrvars.hcl
variable "vm_name" {
@@ -47,12 +55,6 @@ variable "cloud_init" {
default = "vyos"
}
-# if true configure grub to use serial console as default
-variable "grub_serial" {
- type = bool
- default = true
-}
-
# equuleus: debian 11 (branch 1.3.*)
# sagitta: debian 12 (branch 1.4.*)
# circinus: debian 12 (branch 1.5.*)
@@ -89,9 +91,16 @@ variable "sleep_after_grub" {
default = "45" # in seconds
}
+# set grub_serial=1 to turn grub default=1, ie: use serial console. it is need to adjust on hypervisor
+variable "grub_serial" {
+ type = bool
+ default = true
+}
+
locals {
iso_path = "iso/${var.vm_name}.iso"
- output_dir = "output-vyos-${regex_replace(timestamp(), "[: ]", "-")}"
+ output_dir = "output/vyos-image1/${regex_replace(timestamp(), "[: ]", "-")}"
+ #output_dir = "build/image-install"
}
source "qemu" "vyos" {
@@ -120,9 +129,9 @@ source "qemu" "vyos" {
"${var.ssh_password}<enter><wait>",
"<enter><wait10s>", #vda
#"shutdown -h now<enter>"
- "reboot now<enter><wait60s>",
- "${var.ssh_username}<enter><wait>",
- "${var.ssh_password}<enter><wait>",
+ #"reboot now<enter><wait60s>",
+ #"${var.ssh_username}<enter><wait>",
+ #"${var.ssh_password}<enter><wait>",
]
accelerator = "kvm"
@@ -179,86 +188,24 @@ build {
format = "qcow2"
}
- # source "source.raw.vyos" {
- # name = "vyos_qemu_raw"
- # vm_name = "${var.vm_name}-${source.name}.raw"
- # format = "raw"
- # }
-
-
provisioner "shell-local" {
inline = [
+ #"rm -rf ${local.output_dir}",
"mkdir -p ${local.output_dir}"
]
}
- # preparing provisioner
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/init.sh",
- ]
- }
-
- # installing apt repos and custom packages
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' CLOUD_INIT='${var.cloud_init}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/apt-repo-debian.sh",
- "scripts/vyos/apt-repo-vyos.sh",
- "scripts/vyos/apt-install.sh",
- ]
- }
-
- # preparing cloud-init
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' CLOUD_INIT='${var.cloud_init}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/cloud-init-debian.sh",
- "scripts/vyos/cloud-init-vyos.sh",
- ]
- }
-
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' PLATFORM='${var.platform}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/platform-qemu.sh"
- ]
- }
-
- # cleanup before install
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/vyos-install-pre.sh",
- ]
- }
-
- # install vyos on disk
- # provisioner "shell" {
- # execute_command = "VYOS_RELEASE='${var.vyos_release}' VM_NAME='${var.vm_name}' VM_PASSWORD='${var.ssh_password}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- # scripts = [
- # "scripts/vyos/vyos-install.sh",
- # ]
- # }
-
- # cleanup after install
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/vyos-install-post.sh",
- ]
- }
-
post-processor "checksum" {
- checksum_types = ["sha256"]
- keep_input_artifact = true
+ checksum_types = ["sha256"]
+ keep_input_artifact = true
}
post-processors {
post-processor "shell-local" {
inline = [
- "cp '${local.output_dir}/${var.vm_name}-${source.name}.qcow2' iso/${var.vm_name}.qcow2"
+ "cp '${local.output_dir}/${var.vm_name}-${source.name}.qcow2' iso/${var.vm_name}.qcow2",
+ "qemu-img convert -O vpc iso/${var.vm_name}.qcow2 iso/${var.vm_name}.vhd",
+ "cd iso && sha256sum * > SHA256SUM"
]
#only = ["vyos_qemu_qcow2"]
}
diff --git a/vyos-image.pkr.hcl b/vyos-image2.pkr.hcl
index 651f1d4..539c518 100644
--- a/vyos-image.pkr.hcl
+++ b/vyos-image2.pkr.hcl
@@ -1,4 +1,11 @@
-
+packer {
+ required_plugins {
+ qemu = {
+ version = "~> 1"
+ source = "github.com/hashicorp/qemu"
+ }
+ }
+}
# dont edit those vars below, customize in local.auto.pkrvars.hcl using local.example.pkrvars.hcl
@@ -49,12 +56,6 @@ variable "cloud_init" {
default = "vyos"
}
-# if true configure grub to use serial console as default
-variable "grub_serial" {
- type = bool
- default = true
-}
-
# equuleus: debian 11 (branch 1.3.*)
# sagitta: debian 12 (branch 1.4.*)
# circinus: debian 12 (branch 1.5.*)
@@ -91,9 +92,23 @@ variable "sleep_after_grub" {
default = "45" # in seconds
}
+# set grub_serial=1 to turn grub default=1, ie: use serial console. it is need to adjust on hypervisor
+variable "grub_serial" {
+ type = bool
+ default = true
+}
+
+# which kind of datasource should be used
+# nocloud_configdrive => use this as default, will turn on NoCloud, ConfigDrive on cloud-init datasource_list
+# blank - don't set default datasource_list
+variable "cloud_init_datasource" {
+ default = "nocloud_configdrive"
+}
+
locals {
- iso_path = "iso/${var.vm_name}.iso"
- output_dir = "output-vyos-${regex_replace(timestamp(), "[: ]", "-")}"
+ iso_path = "iso/${var.vm_name}.vhd"
+ #iso_path = "iso/vyos-1.3.6.qcow2"
+ output_dir = "output/vyos-image2/${regex_replace(timestamp(), "[: ]", "-")}"
}
source "qemu" "vyos" {
@@ -152,7 +167,8 @@ source "qemu" "vyos" {
["-smp", "4"],
["-cpu", "host"],
["-netdev", "user,id=user.0,", "hostfwd=tcp::{{ .SSHHostPort }}-:22"],
- ["-device", "virtio-net,netdev=user.0"]
+ ["-device", "virtio-net,netdev=user.0"],
+ ["-drive", "file=iso/vyos-1.3.6.qcow2,if=virtio,cache=writeback,discard=ignore,format=qcow2"]
]
}
@@ -165,13 +181,6 @@ build {
format = "qcow2"
}
- # source "source.raw.vyos" {
- # name = "vyos_qemu_raw"
- # vm_name = "${var.vm_name}-${source.name}.raw"
- # format = "raw"
- # }
-
-
provisioner "shell-local" {
inline = [
"mkdir -p ${local.output_dir}"
@@ -186,6 +195,14 @@ build {
]
}
+ # configure vyos
+ provisioner "shell" {
+ execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
+ scripts = [
+ "scripts/vyos/configure.sh",
+ ]
+ }
+
# installing apt repos and custom packages
provisioner "shell" {
execute_command = "VYOS_RELEASE='${var.vyos_release}' CLOUD_INIT='${var.cloud_init}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
@@ -198,13 +215,15 @@ build {
# preparing cloud-init
provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' CLOUD_INIT='${var.cloud_init}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
+ execute_command = "VYOS_RELEASE='${var.vyos_release}' CLOUD_INIT='${var.cloud_init}' CLOUD_INIT_DATASOURCE='${var.cloud_init_datasource}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
scripts = [
"scripts/vyos/cloud-init-debian.sh",
"scripts/vyos/cloud-init-vyos.sh",
+ "scripts/vyos/cloud-init-datasource.sh",
]
}
+ # if PLATFORM=qemu will install qemu packages
provisioner "shell" {
execute_command = "VYOS_RELEASE='${var.vyos_release}' PLATFORM='${var.platform}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
scripts = [
@@ -212,36 +231,28 @@ build {
]
}
- # cleanup before install
- provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
- scripts = [
- "scripts/vyos/vyos-install-pre.sh",
- ]
- }
-
- # install vyos on disk
+ # if grub_serial=1 will install change grub to serial
provisioner "shell" {
- execute_command = "VYOS_RELEASE='${var.vyos_release}' VM_NAME='${var.vm_name}' VM_PASSWORD='${var.ssh_password}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
+ execute_command = "VYOS_RELEASE='${var.vyos_release}' GRUB_SERIAL='${var.grub_serial}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
scripts = [
- "scripts/vyos/vyos-install.sh",
+ "scripts/vyos/grub-serial.sh"
]
}
- # cleanup after install
+ # cleanup
provisioner "shell" {
execute_command = "VYOS_RELEASE='${var.vyos_release}' {{ .Vars }} sudo -E bash '{{ .Path }}'"
scripts = [
- "scripts/vyos/vyos-install-post.sh",
+ "scripts/vyos/cleanup.sh",
]
}
post-processors {
post-processor "shell-local" {
inline = [
- "cp '${local.output_dir}/${var.vm_name}-${source.name}.qcow2' /mnt/pve/svm_privateos_ic1a_main/template/iso/"
+ #"cp '${local.output_dir}/${var.vm_name}-${source.name}.qcow2' /mnt/pve/svm_privateos_ic1a_main/template/iso/"
+ "cp 'iso/vyos-1.3.6.qcow2' /mnt/pve/svm_privateos_ic1a_main/template/iso/"
]
- #only = ["vyos_qemu_qcow2"]
}
}
} \ No newline at end of file
diff --git a/vyos.pkrvars.hcl b/vyos.pkrvars.hcl
index 85a0540..f08d109 100644
--- a/vyos.pkrvars.hcl
+++ b/vyos.pkrvars.hcl
@@ -1,4 +1,15 @@
+# vars:
+# - .env building vars: control building process
+# - vyos.pkrvars.hcl image vars: define image parameters - git default
+# - local.pkrvars.hcl image vars: define image parameters - clone vyos.pkrvars.hcl to override it locally
+
+
+
+ssh_username = "vyos"
+ssh_password = "vyos"
+
+vm_name = "vyos-1.3.6"
# platform = "none" # will not install any specific platform
# - qemu will install qemu-guest-agent
@@ -7,13 +18,38 @@ platform = "qemu"
# cloud-init values:
# debian - will install/replace cloud-init packages
# vyos - will keep cloud-init packages from vyos
+# comment - don't install cloud-init at all
cloud_init = "debian"
-# if true configure grub to use serial console as default
+# which kind of datasource should be used
+# nocloud_configdrive => use this as default, will turn on NoCloud, ConfigDrive on cloud-init datasource_list
+# blank - don't set default datasource_list
+cloud_init_datasource = "nocloud_configdrive"
+
+# Set grub_serial=1 to turn grub default=1, ie: use serial console. it is need to adjust on hypervisor
+#
+# for proxmox:
+# qm set 9000 --serial0 socket --vga serial0
grub_serial = true
# equuleus: debian 11 (branch 1.3.*)
# sagitta: debian 12 (branch 1.4.*)
# circinus: debian 12 (branch 1.5.*)
# current: debian 12 (branch 1.5.*)
-vyos_release = "equuleus" \ No newline at end of file
+vyos_release = "equuleus"
+
+# false will start vnc for console
+headless = false
+
+# in MB (10GB x 1024 = 10240)
+disk_size = 10240
+
+
+
+# todo:
+# - disable/enable ssh
+# - disable/enable dhcp
+# - set interface/gateway
+# - keep vyos/vyos user/password or customize it
+# - customize to install any other agent or package as needed like
+# extra_packages = [] \ No newline at end of file